关于Java多线程你了解多少

人生之路不会是一帆风顺的,我们会遇上顺境,也会遇上逆境,在所有成功路上折磨你的,背后都隐藏着激励你奋发向上的动机,人生没有如果,只有后果与结果,成熟,就是用微笑来面对一切小事。

导读:本篇文章讲解 关于Java多线程你了解多少,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

🏡个人主页 :@ 守夜人st
🚀系列专栏:Java
…持续更新中敬请关注…
🙉博主简介:软件工程专业,在校学生,写博客是为了总结回顾一些所学知识点

多线程

什么是线程?

  • 线程(thread)是一个程序内部的一条执行路径。
  • 启动程序执行后,main方法的执行其实就是一条> > 单独的执行路径
  • 程序中如果只有一条执行路径,那么这个程序就是> 单线程的程序

什么是多线程?

  • 多线程是指从软硬件上实现多条执行流程的技术
  • 多线程的应用十分广泛,例如:消息通信、淘宝、> 京东这样的系统都离不开多线程

多线程的创建

继承Thread类

  1. 定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法

  2. 创建MyThread类的对象

  3. 调用线程对象的start()方法启动线程(启动后还是执行run方法的)

优缺点:

  • 优点:编码简单
  • 缺点:线程类已经继承Thread,无法继承其他类,不利于扩展。
package com.shouyeren.Thread_app;

/**
    目标:多线程的创建方式一:继承Thread类实现
 */
public class ThreadDemo01 {
    public static void main(String[] args) {
        //创建MyThread类的对象
        Thread myThread = new MyThread();

        //调用线程对象的start()方法启动线程
        myThread.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程执行输出" + i);
        }
    }
}

/**
 1. 定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法

 2. 创建MyThread类的对象

 3. 调用线程对象的start()方法启动线程(启动后还是执行run方法的)
 */
class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程执行输出" + i);
        }
    }
}
  1. 为什么不直接调用run方法,而是调用start启动线程?
  • 直接调用run方法会当成普通方法执行,此时相当于还是单线程执行
  • 只有调用start方法启动才是启动一个新的线程执行
  1. 为什么不要把主线程任务放在子线程之前?
    • 这样主线程是一直先跑完的,相当于是一个单线程的效果了

实现Runnable接口

  1. 定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
  2. 创建MyRunnable任务对象
  3. 把MyRunnable任务对象交给Thread处理
  4. 调用线程对象的start()方法启动线程
构造器 说明
public Thread(Sting name) 可以为当前线程指定名称
public Thread(Runnable target) 封装Runnable对象成为线程对象
public Thread(Runnable target,String name) 封装Runnable对象成为线程对象,并为当前线程指定名称
package com.shouyeren.Thread_app;

/**
    目标:多线程的创建方式二:实现MyRunnable接口
 */
public class ThreadDemo02 {
    public static void main(String[] args) {
        //创建一个任务对象
        Runnable target = new MyRunnable();
        //把任务对象交给Thread处理
        Thread thread = new Thread(target);
        //启动线程
        thread.start();
    }
}
class MyRunnable implements Runnable{
    /***
     * 重写run方法,定义线程的执行任务
     */
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程输出:" + i);
        }
    }
}

package com.shouyeren.Thread_app;

/**
    目标:多线程的创建方式二:匿名内部类实现,语法形式
 */
public class ThreadDemo03 {
    public static void main(String[] args) {
        //写法1
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("子线程1输出:" + i);
                }
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();

        //写法2
        new Thread(new Runnable(){
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("子线程2输出:" + i);
                }
            }
        }).start();

        //写法3
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println("子线程3输出:" + i);
            }
        }).start();

        for (int i = 0; i < 10; i++) {
            System.out.println("主线程执行输出:" + i);
        }
    }
}


优缺点:

  • 优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强
  • 缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的。

JDK5新增:实现Callable接口

  1. 得到任务对象

定义实现类Callable接口,重写call方法,封装要做的事情
用FutureTask把Callable对象封装成线程任务对象

  1. 把线程任务对象交给Thread处理
  2. 调用Thread的start方法启动线程,执行任务
  3. 线程执行完毕后,通过FutureTask的get方法去获取任务执行的结果。
方法名称 说明
public FutureTask<>(Callable call) 把Callable对象封装成FutureTask对象
public V get() throws Exception 获取线程执行call方法返回的结果
package com.shouyeren.Thread_app;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * 线程创建的第三种方式:实现Callable接口,结合FutureTask完成
 */
public class ThreadDemo04 {
    public static void main(String[] args) {
        //创建Callanle任务对象
        Callable<String> callable1 = new MyCallable(100);
        //把Callable任务对象交给FutureTask对象
        FutureTask<String> futureTask1 = new FutureTask<>(callable1);
        Thread t1 = new Thread(futureTask1);
        t1.start();

        Callable<String> callable2 = new MyCallable(200);
        FutureTask<String> futureTask2 = new FutureTask<>(callable2);
        Thread t2 = new Thread(futureTask2);
        t2.start();

        try {
            String rs1 = callable1.call();
            System.out.println("第一个返回结果:" + rs1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            String rs2 = callable2.call();
            System.out.println("第二个返回结果:" + rs2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
//定义一个任务类,实现Callable接口,应该声明线程执行返回结果的数据类型
class MyCallable implements Callable<String> {
    private int n;

    public MyCallable(int n) {
        this.n = n;
    }

    //重写call方法
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= n; i++) {
            sum += i;
        }
        return "子线程的执行结果是:" + sum;
    }
}

创建线程的3种方式对比

方法 优点 缺点
继承Thread类 编程比较简单,可以直接使用Thread中的方法 扩展性较差,不能再继续继承其他的类,不能返回执行结果
实现Runnable接口 扩展性强,实现该接口的同时还能继承其他的类 编程相对复杂,不能返回执行结果
实现Callable接口 扩展性强,实现该接口的同时还可以继承其他的类,并且可以得到返回的执行结果 编程相对复杂

Thread的常用方法

Thread的构造器

方法名称 说明
public Thread (String name) 可以为当前线程指定名称
public Thread (Runnable target) 封装Runnable对象成为线程对象
public Thread(Runnable target ,String name) 封装Runnable对象成为线程对象,并指定线程名称
方法名称 说明
public final String getName() 获取当前线程的名称,默认是Thread-索引
public final synchronized void setName(String name) 修改当前线程的名称
public static native Thread currentThread(); 获取当前线程
public static void sleep(long time) 让当前线程休眠指定的时间后继续执行,单位毫秒

线程安全

线程安全的问题是什么,发生的原因?

多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,成为线程安全问题。

线程安全问题出现的原因?

  • 存在多线程并发
  • 同时访问共享资源
  • 存在修改共享资源

线程安全问题案例模拟

案例:取钱业务

小明和小红是一对夫妻,他们有一个共同的账户,余额10万元,模拟两个人同时取钱10万元

分析:

  1. 需要提供一个账户类,创建一个账户对象表示两人的共享账户
  2. 需要定义一个线程类,线程类可以处理账户对象
  3. 创建两个线程对象,传入同一个账户对象。
  4. 启动了两个线程,去同一个账户对象中取钱10万元
package com.shouyeren.Thread_app.Thread_safe;

public class ThreadDemo {
    public static void main(String[] args) {
        //创建一个共享账户对象
        Account account = new Account(100000);
        //创建两个线程对象,代表小明和小红
       new DrawThread(account,"小明").start();
       new DrawThread(account,"小红").start();


    }
}

package com.shouyeren.Thread_app.Thread_safe;

public class Account {
    private double money;

    public Account(double money) {
        this.money = money;
    }

    public Account() {
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public void DrawMoney(double money) {
        String name = Thread.currentThread().getName();
        //判断余额
        if (this.money >= money){
            System.out.println(name + "取钱成功,吐出" + money);
            //更新余额
            this.money -= money;
            System.out.println("余额:" + this.money);
        }else {
            System.out.println("余额不足");
        }
    }
}

package com.shouyeren.Thread_app.Thread_safe;

public class DrawThread extends Thread{
    private Account account;
    public DrawThread(Account account,String name){
        this.account = account;
    }
    @Override
    public void run() {
        account.DrawMoney(100000);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-juj0F0pC-1677676685836)(C:\Users\shouyeren\AppData\Roaming\Typora\typora-user-images\image-20230226203037611.png)]

线程同步

线程同步就是为了解决线程安全问题

核心思想:

  • 加锁,把共享资源进行上锁,每次只能一个线程进入访问完毕以后解锁,然后其他线程才能进来

方式一:同步代码块

  • 作用:把出现线程安全问题的核心代码给上锁

  • 原理:每次只能进入一个线程,执行完毕后自动解锁,其他线程才可以进来执行

    synchronized(同步锁对象){
        操作共享资源的核心代码;
    }
    
    package com.shouyeren.Thread_app.Thread_safe;
    
    public class Account {
        private double money;
    
        public Account(double money) {
            this.money = money;
        }
    
        public Account() {
        }
    
        public double getMoney() {
            return money;
        }
    
        public void setMoney(double money) {
            this.money = money;
        }
    
        public void DrawMoney(double money) {
            String name = Thread.currentThread().getName();
            //判断余额
            synchronized (this) {
                if (this.money >= money){
                    System.out.println(name + "取钱成功,吐出" + money);
                    //更新余额
                    this.money -= money;
                    System.out.println("余额:" + this.money);
                }else {
                    System.out.println("余额不足");
                }
            }
        }
    }
    
    

    锁对象要求:

    • 理论上:锁对象只要对于当前同时执行的线程来说是同一个对象即可。
    • 锁对象用任意唯一的对象会影响其他无关线程的执行
    • 建议使用共享资源作为锁对象
    • 对于实例方法建议使用this作为锁对象
    • 对于静态方法建议使用字节码(类名.class)对象作为锁对象

方式二:同步方法

作用:把出现线程安全问题的核心方法给上锁

原理:每次只能进入一个线程,执行完毕后自动解锁,其他线程才可以进来执行

修饰符 synchronized 返回值类型 方法名称(形参列表){
    操作共享资源的代码
}
public synchronized void DrawMoney(double money) {
        String name = Thread.currentThread().getName();
        //判断余额
        if (this.money >= money){
            System.out.println(name + "取钱成功,吐出" + money);
            //更新余额
            this.money -= money;
            System.out.println("余额:" + this.money);
        }else {
            System.out.println("余额不足");
        }
    }

底层原理:同步方法其实底层也是有隐式锁的,只是锁的范围是整个方法代码。

如果方法是实例方法,同步方法默认用this作为锁对象,但是代码要高度面向对象。

如果方法是静态方法,同步方法默认用类名.class作为锁对象

方式三:Lock锁

  • 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock,更加灵活、方便

  • Lock实现提供比使用synchronzied方法和语句可以获得更广泛的锁定操作

  • Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来构建Lock锁对象

方法名称 说明
public ReentrantLock() 获得Lock锁的实现类对象
方法名称 说明
void lock() 获得锁
void unlock() 释放锁
private final ReentrantLock lock = new ReentrantLock();

public void m(){
    lock.lok();//上锁
    try{
        method body...
    }finally{
        lock,unlock();//释放锁
    }
}

线程通信

什么是线程通信、如何实现

  • 所谓线程通信就是线程间相互发送数据,线程通信通常通过共享一个数据的方式实现。
  • 线程间会根据共享数据的情况决定自己该怎么做,以及通知其他线程该怎么做

线程通信常见模型

  • 生产者与消费者模型:生产者线程负责生产数据,消费者负责消费数据
  • 要求:生产者线程生产完成数据后,唤醒消费者,然后等待自己;消费者消费完该数据后,唤醒生产者,然后等待自己
  • 线程通信是在多个线程操作同一共享资源的时候进行,前提是保证线程安全。

Object类的等待和唤醒方法

方法名称 说明
void wait() 让当前线程等待并释放所占用锁,直到另一个线程调用notify()方法或notifyAll()方法
void notify() 唤醒正在等待的单个线程
void notifyAll() 唤醒正在等待的所有线程

注意:

  • 上述方法应当使用当前同步锁对象

生产者消费者模型模拟

package com.shouyeren.Thread_app.Thread_comunication;

public class Commodity {
    private double quantity;

    public Commodity() {
    }
    public Commodity(double quantity) {
        this.quantity = quantity;
    }

    public synchronized void consumption(double quantity) {
        String name = Thread.currentThread().getName();
        try {
            if (this.quantity >= quantity){
                this.quantity -= quantity;
                System.out.println(name + "购买商品成功,仓库剩余产品数量为:" + this.quantity);
                this.notifyAll();
                this.wait();
            }else {
                this.notifyAll();
                this.wait();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public synchronized void production(double quantity) {
        String name = Thread.currentThread().getName();
        try {
            if (this.quantity == 0){
                this.quantity += quantity;
                System.out.println(name + "生产商品并存入仓库成功,仓库剩余产品数量为:" + this.quantity);
                this.notifyAll();
                this.wait();
            }else {
                this.notifyAll();
                this.wait();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public double getQuantity() {
        return quantity;
    }

    public void setQuantity(double quantity) {
        this.quantity = quantity;
    }



}

package com.shouyeren.Thread_app.Thread_comunication;



public class ProductionThread extends Thread{
  private Commodity commodity;
  public ProductionThread(Commodity commodity, String name){
      super(name);
      this.commodity = commodity;
    }
    @Override
    public void run() {
       while (true){
           commodity.production(100000);
           try{
               Thread.sleep(3000);
           }catch (Exception e){
               e.printStackTrace();
           }
       }
    }
}

package com.shouyeren.Thread_app.Thread_comunication;



public class DrawThread extends Thread{
  private Commodity commodity;
  public DrawThread(Commodity commodity, String name){
      super(name);
      this.commodity = commodity;
    }
    @Override
    public void run() {
       while (true){
           commodity.consumption(100000);
           try{
               Thread.sleep(3000);
           }catch (Exception e){
               e.printStackTrace();
           }
       }
    }
}

package com.shouyeren.Thread_app.Thread_comunication;

/**
 * 模拟生产者消费者模型
 */
public class ThreadDemo {
    public static void main(String[] args) {
        Commodity commodity = new Commodity(0);
        new DrawThread(commodity,"消费者1号").start();
        new DrawThread(commodity,"消费者2号").start();

        new ProductionThread(commodity,"生产者1号").start();
        new ProductionThread(commodity,"生产者2号").start();
        new ProductionThread(commodity,"生产者3号").start();


    }
}

线程池

线程池就是一个可以复用线程的技术

如果用户每发起一个请求,后台就建立一个新线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的,这样会严重影响系统的性能。

线程池实现的API、参数说明

  • JDK 5.0起提供了代表线程池的接口:ExecutorService

如何得到线程池对象

  • 方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
  • 方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象

ThreadPoolExecutor构造器的参数说明

public ThreadPoolExecutor(int corePoolSize,
                         int maximumPoolSize,
                         long KeepAliveTime,
                         TimeUnit unit,
                         BlockingQueue<Runnable> workQueue,
                         ThreadFactory threadFactory,
                         RejectedExecutionHandler handler)
  • 参数一:指定线程池的线程数量(核心线程):coreOpplSize——不能小于0
  • 参数二:指定线程池可支持的最大线程数:maximumPoolSize——最大数量>=核心线程数量
  • 参数三:指定零食线程的最大存活时间:KeepAliveTime——不能小于0
  • 参数四:指定存活时间的单位(秒,分,时,天):unit——时间单位
  • 参数五:指定任务队列:workQueue——不能为NULL
  • 参数六:指定用哪个线程工厂创建线程:threadFactory——不能为NULL
  • 参数七:指定线程忙,任务满时,新任务来了怎么办:handler——不能为NULL

常见面试题

临时线程什么时候创建?

  • 新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程

什么时候会开始拒绝任务?

  • 核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务

线程池处理Runnable任务

ExecutorService pools = new ThreadPoolExecutor(3,5,8,TimeUnit.SECONDS,new ArrayBlockingQueue<>(6),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

ExecutorService的常用方法

方法名称 说明
void execute(Runnable command) 执行任务/命令,无返回值,一般用来执行Runnable任务
Future submit(Callable task) 执行任务,返回未来任务对象获取线程结果,一般用来执行Callable任务
void shutdown() 等任务执行完之后关闭线程
List shutdownNow() 立刻关闭,停止正在正在执行的任务,并返回队列中未执行的任务

新任务拒绝策略

策略 详解
ThreadPoolExecutor.AbortPolicy 丢弃任务并抛出RejectedExecutionException异常,是默认的策略
ThreadPoolExecutor.DiscardPolicy: 丢弃任务,但是不抛出异常,这是不推荐的做法
ThreadPoolExecutor.DiscardOldesPolicy 抛弃队列中等待最久任务,然后把当前任务加入队列中
ThreadPoolExecutor.CallerRunsPolicy 由主线程负责调用任务的run()方法从而绕过线程池直接执行
package com.shouyeren.Thread_app.pool;

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "输出了:hello word ==>" + i);
        }
    }
}

package com.shouyeren.Thread_app.pool;

import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService pool = new ThreadPoolExecutor(3,5,6,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        Runnable myRunnable = new MyRunnable();
        pool.execute(myRunnable);
        pool.execute(myRunnable);
        pool.execute(myRunnable);

    }
}

线程池处理Callable任务

package com.shouyeren.Thread_app.pool;

import java.util.concurrent.Callable;

//定义一个任务类,实现Callable接口,应该声明线程执行返回结果的数据类型
class MyCallable implements Callable<String> {
    private int n;

    public MyCallable(int n) {
        this.n = n;
    }

    //重写call方法
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= n; i++) {
            sum += i;
        }
        return Thread.currentThread().getName() + "子线程的执行结果是:" + sum;
    }
}
package com.shouyeren.Thread_app.pool;

import java.util.concurrent.*;

public class ThreadPoolDemo_Callable {
    public static void main(String[] args) throws Exception{
        ExecutorService pool = new ThreadPoolExecutor(3,5,6,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));
        Future<String> f4 = pool.submit(new MyCallable(400));

        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());

    }
}

Executors得到线程池对象的常用方法

Executors:线程池的工具类通过调用方法返回不同类型的线程池对象

方法名称 说明
public static ExecitorService newCachedThreadPool() 线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了一段时间则会被回收掉
public static ExecutorService newCachedThreadPool(int nThreads) 创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代他
public static ExecitorService newSingleThreadExecutor() 创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个线程池,可以实现给定的延迟后运行任务,或者定期执行任务

注意:Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的。

Executors使用可能存在的陷阱

  • 大型并发系统环境中使用Executors如果不注意,可能会出现系统风险

public static ExecutorService newFixedThreadPool(int nThreads)

public static ExecutorService newSingleThreadExecutor()

允许请求任务队列的长度是Integer,MAX_VALUE,可能出现OOM错误(java.lang.OutOfMemoryError)

public static ExecutorService newCachedThreadPool()

public static ScheduExecutorService newScheduThreadPool(int corepoolSize)

创建的线程数量最大上限是Integer,MAX_VALUE,线程数可能会随着任务1:1增长,也可能出现OOM错误(java.lang.OutOfMemoryError)

定时器

定时器是一种控制任务延迟调用,或者周期调用的技术(闹钟、定时邮件发送)

定时器实现的方式:

  • 方式一:Timer

    构造器 说明
    public Timer() 创建Timer定时器对象
    方法 说明
    public void schedule(TimerTask task,long delay,long period) 开启一个定时器。按照计划处理TimerTask任务

    Timer定时器的特点和存在的问题

    1. Timer是单线程,处理多个任务按照顺序执行,存在延时与设置定时器的时间有出入
    2. 可能因为其中的某个任务的异常使Timer线程死亡,从而影响后续任务执行
  • 方式二:ScheduledExecutorService

ScheduledExecutorService是jdk1.5中引入的并发包,目的是为了弥补Timer的缺陷,ScheduledExecutorService内部为线程池

Executors的方法 说明
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 得到线程池对象
ScheduledExecutorService的方法 说明
public ScheduledFuture<?> scheduledRate(Runnable command,long initialDelay,long period,TimeUnit unit) 周期调度方法

ScheduledExecutorService的优点

基于线程池,某个任务的执行情况不会影响其他定时任务的执行。

并发与并行

正在运行的程序(软件)就是一个独立的进程,线程是属于进程的,多个线程其实是并发与并行同时进行的

并发的理解:

  • CPU同时处理线程的数量有限
  • CPU会轮询为系统每个线程服务,由于CPU切换的速度很快,给我们感觉这些线程在同时进行,这就是并发
    并行的理解:
  • 在同一个时刻上,同时有多个线程在被CPU处理并执行

生命周期

线程的状态:也就是线程从生到死的过程,以及中间经历的各种状态及状态切换

理解线程的状态有利于提升并发编程的理解能力

Java线程的状态:

  • Java总共定义了6种状态
  • 6种状态都定义在Thread类的内部枚举类中
public class Thread{
    public enum State{
        NEW,//新建
        RUNABLE,//可运行
        BLOCKED,//阻塞
        WAITING,//无限等待
        TIMED_WAITING,//计时等待
        TERMINATED;//被终止下
    }
}

在这里插入图片描述


感觉不错的话,动手点个赞吧!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/125471.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!