Java 多线程学习(一)

简介: Java 多线程学习

1、线程简介

1.1 多任务理解

我们举例来理解下多任务;

开车 + 打电话

吃饭 + 玩手机

这些动作都可以抽象为任务,虽然看起来一心二用,但人只有一个大脑,在一个时间片刻只能处理一个任务。

CPU 也是一样,面对多个任务,只能在一个时间片刻处理一个任务。

1.2 多线程理解

我们举例来理解下多线程;

多条线路同时跑起来;有多个分支共同去执行;

主线程调用 run 方法和调用 start 方法开启子线程的区别如下图所示。

1.3 线程与进程

执行程序的过程,叫进程;一个进程中有多个线程;

核心概念;

  • 线程就是独立的执行路径;
  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,GC 线程;
  • main()称之为主线程,为系统的入口,用于执行整个程序;
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预。
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
  • 线程会带来额外的开销,如 CPU 调度时间,并发控制开销。
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

2、线程实现

线程的三种实现方式:(线程实现在实际工作中优先使用Runnable接口和Callable接口方式

2.1 第一种:继承 Thread 类,重写 run 方法

继承 Thread 类,重写 run 方法。创建这个类的对象,再调用 start() 即可

package com.example.democrud.democurd.test01;
/**
 * @author 闫文超
 */
//多线程调用 先继承Thred 方法 重写run方法 最后再用start 开启线程
//总结:线程的开启不一定立即开始执行,由CPU调度执行
public class testThread01 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习多线程---" + i);
        }
    }
    public static void main(String[] args) {
        //创建一个线程对象
        testThread01 thread01 = new testThread01();
        //先执行run的方法在执行下面的内容
        // thread01.run();
        //start的方法和main同步执行;顺序同步
        // 调用start 开启线程
        thread01.start();
        for (int i = 0; i < 200; i++) {
            System.out.println("我在学习thread--" + i);
        }
    }
}

下载文件需要在 pom.xml 中 commons io 包。

<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>
package com.example.democrud.democurd.test01;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class testThred02 extends Thread {
    public String url;//图片路径
    public String name;//图片的名字
    public testThred02(String url, String name) {
        this.name = name;
        this.url = url;
    }
    @Override //代表重写
    public void run() {
        downloadpng down = new downloadpng();
        down.download(url, name);
        System.out.println("下载的文件名为" + name);
    }
    //主线程 main方法
    public static void main(String[] args) {
        testThred02 thred01 = new testThred02("https://img1.baidu.com/it/u=413643897,2296924942&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1680541200&t=26be472b80013591100109eb63c7c5ec", "test01.jpg");
        testThred02 thred02 = new testThred02("https://img1.baidu.com/it/u=307074048,654359288&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1680541200&t=554384a7bbd003adba3de4aaa73365d4", "test02.jpg");
        testThred02 thred03 = new testThred02("https://img1.baidu.com/it/u=307074048,654359288&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1680541200&t=554384a7bbd003adba3de4aaa73365d4", "test03.jpg");
        //开启线程
        thred01.start();
        thred02.start();
        thred03.start();
    }
    //先写一个下载器
    class downloadpng {
        //引用地址和名字进行下载
        public void download(String url, String name) {
            try {
                FileUtils.copyURLToFile(new URL(url), new File(name));
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("IO异常,downloader 方法出现问题");
            }
        }
    }
}

使用该方法下载网络图片。

2.2 第二种:

继承 Runnable 接口,创建 Tread 对象
或者使用Runnable接口通过线程池
下面演示的是继承 Runnable 接口,创建 Tread 对象:
继承 Runnable 接口,创建 Tread 对象,传入实现类,开启 start 方法

package com.example.democrud.democurd.test01;
//1.实现Runnable接口 重写run方法 执行线程需要丢人Runnable接口实线路调用start方法启动线程
public class testThread03 implements Runnable {
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("闫文超在摸鱼啊---" + i);
        }
    }
    public static void main(String[] args) {
        //创建runnable接口的实现类对象
        testThread03 thread03 = new testThread03();
        //创建线程对象,通过线程对象来开启我们的线程;代理
        Thread thread = new Thread(thread03);
        thread.start();
        // 或者
       // new Thread(thread03).start();
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习当当中" + i);
        }
    }
}

以上两种方式的比较:

继承 Thread 类

  • 子类继承 Thread 类具备多线程能力
  • 启动线程:子类对象 .start()
  • 不建议使用:避免 OOP 单继承局限性

实现 Runnable 接口

  • 实现接口 Runnable 具有多线程能力
  • 启动线程:传入目标对象+Thread对象.start()
  • 推荐使用:避免单继承局限性,方便同一个对象被多个线程使用。

火车抢票实例:

Runnable 实现多线程,创造一个实列 ticketRunnable ,可共享给多个线程。

package com.example.democrud.democurd.test01;
//实现Runnnable的方式调用多线程
//多个线程调用一个对象
//    买火车票的例子
//    发现问题:多个线程操作同一个资源,线程不安全,数据紊乱!
public class testThread04 implements Runnable{
    public int ticketNums=30;
    @Override
    public void run() {
        while (true){
            if (ticketNums<=10){
                break;
            }
            try {
                //线程模拟休眠2s
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
        }
    }
    public static void main(String[] args) {
        testThread04 thread04 = new testThread04();
        new Thread(thread04,"小王").start();
        new Thread(thread04,"小龙").start();
        new Thread(thread04,"小李").start();
        new Thread(thread04,"黄牛").start();
    }
}

龟兔赛跑案例:

package com.example.democrud.democurd.test01;
//实现runnable的接口
public class testThred05 implements Runnable {
    //胜利者
    public static String winner;
    @Override
    public void run() {
        for (int i = 0; i < 1001; i++) {
            if (Thread.currentThread().getName().equals("兔子") && i % 10 == 0) {
                //每跑10步 兔子休眠0.5毫秒
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            Boolean gameover = gameover(i);
            if (gameover)
                break;
            System.out.println(Thread.currentThread().getName() + "-跑了" + i);
        }
    }
    public Boolean gameover(int steps) {
        if (winner != null) {
            return true;
        } else {
            if (steps >= 1000) {
                winner = Thread.currentThread().getName();
                System.out.println("winner is" + winner);
                return true;
            }
        }
        return false;
    }
    public static void main(String[] args) {
        testThred05 thred05 = new testThred05();
        new Thread(thred05, "兔子").start();
        new Thread(thred05, "乌龟").start();
    }
}


相关文章
|
5天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
18 2
|
9天前
|
存储 缓存 Java
java线程内存模型底层实现原理
java线程内存模型底层实现原理
java线程内存模型底层实现原理
|
10天前
|
Java 开发者
Java中的多线程基础与应用
【9月更文挑战第22天】在Java的世界中,多线程是一块基石,它支撑着现代并发编程的大厦。本文将深入浅出地介绍Java中多线程的基本概念、创建方法以及常见的应用场景,帮助读者理解并掌握这一核心技术。
|
6天前
|
Java 调度
Java-Thread多线程的使用
这篇文章介绍了Java中Thread类多线程的创建、使用、生命周期、状态以及线程同步和死锁的概念和处理方法。
Java-Thread多线程的使用
|
13天前
|
Java
领略Lock接口的风采,通过实战演练,让你迅速掌握这门高深武艺,成为Java多线程领域的武林盟主
领略Lock接口的风采,通过实战演练,让你迅速掌握这门高深武艺,成为Java多线程领域的武林盟主
22 7
|
11天前
|
Java 程序员
Java中的多线程基础与实践
【9月更文挑战第21天】本文旨在引导读者深入理解Java多线程的核心概念,通过生动的比喻和实例,揭示线程创建、同步机制以及常见并发工具类的使用。文章将带领读者从理论到实践,逐步掌握如何在Java中高效地运用多线程技术。
|
9天前
|
Java 调度 开发者
Java中的多线程编程:从基础到实践
本文旨在深入探讨Java多线程编程的核心概念和实际应用,通过浅显易懂的语言解释多线程的基本原理,并结合实例展示如何在Java中创建、控制和管理线程。我们将从简单的线程创建开始,逐步深入到线程同步、通信以及死锁问题的解决方案,最终通过具体的代码示例来加深理解。无论您是Java初学者还是希望提升多线程编程技能的开发者,本文都将为您提供有价值的见解和实用的技巧。
15 2
|
11天前
|
Java 数据处理
Java中的多线程编程:从基础到实践
本文旨在深入探讨Java中的多线程编程,涵盖其基本概念、创建方法、同步机制及实际应用。通过对多线程基础知识的介绍和具体示例的演示,希望帮助读者更好地理解和应用Java多线程编程,提高程序的效率和性能。
19 1
|
4天前
|
Java 数据中心 微服务
Java高级知识:线程池隔离与信号量隔离的实战应用
在Java并发编程中,线程池隔离与信号量隔离是两种常用的资源隔离技术,它们在提高系统稳定性、防止系统过载方面发挥着重要作用。
5 0
|
6天前
|
Java 数据处理 调度
Java中的多线程编程:从基础到实践
本文深入探讨了Java中多线程编程的基本概念、实现方式及其在实际项目中的应用。首先,我们将了解什么是线程以及为何需要多线程编程。接着,文章将详细介绍如何在Java中创建和管理线程,包括继承Thread类、实现Runnable接口以及使用Executor框架等方法。此外,我们还将讨论线程同步和通信的问题,如互斥锁、信号量、条件变量等。最后,通过具体的示例展示了如何在实际项目中有效地利用多线程提高程序的性能和响应能力。
下一篇
无影云桌面