Java 线程的五种状态 与 创建线程

Java 线程的 5 种状态

线程状态图:

线程共包含以下五种状态:
1、新建状态(New)线程对象被创建后,就进入了新建状态,例如,Thread thread = new Thread();

2、就绪状态(Runnable):也称之为“可执行”状态。线程对象被创建后,其它线程调用该对象的start()方法,从而启动该线程。例如:thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。

3、运行状态(Running)线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。如果在给定的时间片内没有执行完成,就会被系统换下来等待下一次的执行。

4、阻塞状态(Blocked)阻塞状态是线程因为某种原因让出CPU并暂时停止运行,进入阻塞状态。在阻塞状态的线程不能直接进入就绪状态,只有当引起阻塞的原因消除时,线程才转入到就绪状态,然后重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续执行。

阻塞的情况分三种

  • (1)等待阻塞:通过调用线程的wait()方法,让线程等待某工作的完成;
  • (2)同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
  • (3)其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或超时、或者I/O处理完毕,线程重新转入到就绪状态。

5、死亡状态(Dead):死亡状态是线程生命周期中最后一个阶段。线程死亡的原因有三个,一是正常运行的线程执行完成;二是线程抛出未捕获的异常;三是线程被强制终止,如调用stop()方法来终止一个线程【不推荐使用】。并且已死亡的线程不可以使用start()复活。

获取线程基本信息的方法

1、static Thread currentThread() 返回目前正在执行的线程
2、final String getName() 返回线程的名称
3、final boolean isAlive() 判断线程是否处于活动状态
4、final int getPriority() 获取线程的优先级
5、final void setPriority(int priority) 设置线程的优先级

Java创建线程

Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的对象。

1、继承Thread类

  • (1)定义Thread类的子类,并重写run()方法。run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体;
  • (2)创建Thread子类的实例,即创建了线程对象
  • (3)调用线程对象的start()方法来启动线程
  • 注意:使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量。

2、实现Runnable接口

  • (1)定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体;
  • (2)创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象;
  • (3)调用线程对象的start()方法来启动该线程;
  • 注意:实际的线程对象依然是该Thread实例,只是该Thread实例负责执行其target的run()方法
  • 注意:采用Runnable接口的方式创建的多个线程可以共享线程类的实例变量,这是因为在这种方式下,程序所创建的Runnable对象只是线程的target,而多个线程可以共享同一个target所以多个线程可以共享同一个线程类(实际上是线程的target类)的实例变量

3、使用Callable和Future创建线程

前言:C#可以把任意方法包装成线程执行体,包括有返回值的方法;但Java不可以把任意方法都包装成线程执行体。从Java 5开始,Java提供了Callable接口,该方法提供了一个call()方法可以作为线程执行体,call()方法可以有返回值,可以声明抛出异常。Java 5提供Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供了一个FutureTask实现类,该实现类实现了Future接口,并实现了Runnable接口 ==》 可以作为Thread类的target。

  • 1、创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法有返回值,再创建Callable实现类的实例。
  • 2、使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值
  • 3、创建并调用FutureTask对象作为Thread对象的target,创建并启动新线程
  • 4、调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
//先使用lamdba表达式创建实现Callable<Integer>接口的匿名内部类,重写call方法
//使用FutureTask来包装Callable对象
FutureTask<Integer> task = new FutureTask<<>((Callable<Integer>)()->{
    var i=1;
    System.out.println(Thread.currentThread().getName()+"变量i="+i);
    //call()方法可以有返回值
    return i+1;
});
//实质还是以Callable对象来创建并启动线程
new Thread(task,"有返回值的线程").start();
try{
    //获取线程返回值
    System.out.println("子线程的返回值:"+task.get());
}catch(Exception e){
    e.printStackTrace();
}

注意:Callable接口有泛型限制,Callable接口里的泛型形参类型与call()方法返回值类型相同,且Callable接口是函数式接口,因此使用Lambda表达式来创建Callable对象。

posted @   紫薇哥哥  阅读(544)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程
点击右上角即可分享
微信分享提示