一篇文章带你学会多线程

线程简介(了解)

程序、进程、线程
程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指段静态的代码,静态对象。
进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态过程:有它自身的产生、存在和消亡的过程。——生命周期
线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
并行、并发
并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。
使用多线程的优点

  1. 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
  2. 提高计算机系统CPU的利用率
  3. 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

实现线程的两种方式(掌握)

继承Thread类

  1. 创建一个继承Thread类的子类
  2. 重写Thread类的run()
  3. 实例化子类对象
  4. 调用Thread类的start()
public class ThreadTest extends Thread{
    
    

    public int count =10;
    @Override
    public void run() {
    
    
        while (true){
    
    
            System.out.print(count+" ");
            if(--count==0){
    
    
                break;
            }
        }
    }

    public static void main(String[] args) {
    
    
        new ThreadTest().start();
    }
}

实现Runnable接口

  • 创建一个实现Runnable接口的类
  • 实现Runnable的抽象方法:run()
  • 创建实现类对象
  • 将实现类对象作为参数传递Thread类构造器中,创建Thread对象
  • 调用Thread类的start()

线程的生命周期(掌握)

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
在这里插入图片描述

  • 新建状态
    使用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序start()这个线程。
  • 就绪状态
    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
  • 运行状态
    如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
  • 阻塞状态
    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
    。等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
    。同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
    。其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
  • 死亡状态
    一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

线程的优先级(了解)

每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。

Thread类的常用方法(会用)

常用方法(重要)

序号 方法描述
1 public void start() 使该线程开始执行,Java虚拟机调用run()方法。
2 public void run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
3 public final void setName(String name) 设置线程名称,与创建线程调用Thread类构造器传name参数功能相同。
4 public String getName() 获取线程名称 。
5 public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。
6 public static void sleep(long time) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。单位:毫秒数。
7 public static Thread currentThread() 返回对当前正在执行的线程对象的引用。
8 public final void join(long time) 等待该线程终止的时间最长为 time 毫秒。

不常用方法(了解)

序号 方法描述
1 public final void setPriority(int priority) 更改线程的优先级。
2 public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。
3 public void interrupt() 中断线程。
4 public final boolean isAlive() 测试线程是否处于活动状态 。
5 public static void dumpStack() 将当前线程的堆栈跟踪打印至标准错误流。

线程安全问题(重点)

出现线程安全的问题需要一个前提 : 多个线程同时操作同一个资源

线程执行调用方法run,同一个资源是堆内存的

同步代码块

同步代码块可以解决线程安全问题 : 格式 synchronized关键字

synchronized(任意对象){
    
    
    //线程操作的共享资源
}

任意对象 : 在同步中这个对象称为对象锁,简称锁,官方的稳定称为 对象监视器

同步方法

当一个方法中,所有代码都是线程操作的共享内容,可以在方法的定义上添加同步的关键字 synchronized , 同步的方法,或者称为同步的函数.

  • 同步方法中有对象锁吗 , this对象
  • 静态同步方法中有对象锁吗,锁对象是本类.class属性. 这个属性表示这个类的class文件的对象.
    @Override
    public void run() {
    
    
        while (true)
          sale();
    }
 private static synchronized void sale(){
    
    
    //  synchronized (Ticket.class) {
    
    
    if (tickets > 0) {
    
    
    try {
    
    
        Thread.sleep(20);//线程休眠,暂停执行
        } catch (Exception ex) {
    
    
    }
    System.out.println(Thread.currentThread().getName() + " 出售第" + tickets + "张");
    tickets--;
    }
//  }
}

JDK5新特性Lock锁(重点)

JDK5新的特性 : java.util.concurrent.locks包. 定义了接口Lock.

Lock接口替代了synchronized,可以更加灵活

  • Lock接口的方法
    • void lock() 获取锁
    • void unlock()释放锁
  • Lock接口的实现类ReentrantLock
/**
 *  优化为juc包的接口Lock
 */
public class Ticket implements Runnable {
    
    

    //定义票源
    private  int tickets = 100;
    //获取Lock接口的实现类对象
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
    
    
        while (true)
          sale();
    }

    private void sale(){
    
    
        //获取锁
        lock.lock();
        if (tickets > 0) {
    
    
            try {
    
    
                Thread.sleep(20);//线程休眠,暂停执行
            } catch (Exception ex) {
    
    
            }
            System.out.println(Thread.currentThread().getName() + " 出售第" + tickets + "张");
            tickets--;
        }
        //释放锁
        lock.unlock();
    }
}

线程池ThreadPool(掌握)

线程的缓冲池,目的就是提高效率. new Thread().start() ,线程是内存中的一个独立的方法栈区,JVM没有能力开辟内存空间,和OS交互.

JDK5开始内置线程池

Executors类

  • 静态方法static newFixedThreadPool(int 线程的个数)

    • 方法的返回值ExecutorService接口的实现类,管理池子里面的线程
  • ExecutorService接口的方法

    • submit (Runnable r)提交线程执行的任务

Callable接口

实现多线程的程序 : 接口特点是有返回值,可以抛出异常 (Runnable没有)

抽象的方法只有一个, call

启动线程,线程调用重写方法call

  • ExecutorService接口的方法
    • submit (Callable c)提交线程执行的任务
    • Future submit()方法提交线程任务后,方法有个返回值 Future接口类型
    • Future接口,获取到线程执行后的返回值结果
public class MyCall implements Callable<String> {
    
    
    public String call() throws Exception{
    
    
        return "返回字符串";
    }
}
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        //创建线程池,线程的个数是2个
       ExecutorService  es =  Executors.newFixedThreadPool(2);
       //线程池管理对象service,调用方法啊submit提交线程的任务
        MyRunnable my = new MyRunnable();
        //提交线程任务,使用Callable接口实现类
        Future<String> future = es.submit(new MyCall());//返回接口类型 Future
        //接口的方法get,获取线程的返回值
        String str = future.get();
        System.out.println("str = " + str);
    }

猜你喜欢

转载自blog.csdn.net/weixin_44226883/article/details/121324336