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对象。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程