Java—线程的生命周期及线程控制方法详解
关注微信公众号:CodingTechWork,一起学习进步。
线程生命周期5种状态
介绍
线程的生命周期经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Bolocked)和死亡(Dead)
状态转换图
新建(New)
程序使用new关键字
创建一个线程之后,该线程就处于新建状态
,仅仅由Java虚拟机为其分配内存,并初始化其成员变量的值。不会执行线程的线程执行体。如Thread thread = new Thread()
。
就绪(Runnable)
也称为“可执行状态”
,线程对象调用start()
方法后,该线程处于就绪状态
。如thread.start()
。Java虚拟机会为其创建方法调用栈和程序计数器(线程私有),处于就绪状态的线程并没有开始运行,只是表示该线程可以运行,线程何时运行取决于JVM中线程调度器的调度。
运行(Running)
处于就绪状态的线程获得CPU,开始执行run()方法
的线程执行体,则该线程处于运行状态
。(注意:线程只能从就绪状态进入到运行状态)
阻塞(Boloked)
阻塞状态是线程因为某种原因放弃了CPU的使用权
,暂时停止运行,直到线程进入就绪状态,才有机会转到运行状态。当调用sleep()
、一个阻塞式IO方法
、同步锁、等待通知、suspend()方法
挂起都会使线程进入阻塞状态。
- 线程调用
sleep()
方法主动放弃所占用的处理器资源; - 线程调用一个阻塞式
IO方法
,在该方法返回之前,该线程被阻塞; - 线程试图获得一个
同步监视器
,但该同步监视器正被其他线程所持有; - 线程在等待(
wait()
)某个通知(notify()
); - 程序调用了线程的
suspend()
方法将该线程挂起,但这个方法易造成死锁,应该避免使用。
线程从阻塞状态解除——进入就绪状态的过程:
- 调用
sleep()方法
的线程经过了指定时间; - 线程调用的阻塞式IO方法已经返回;
- 线程成功地获得试图取得的同步监视器(锁);
- 线程正在等待某个通知时,其他线程发出了一个通知;
- 处于挂起状态的线程被调用了
resume()
恢复方法。
死亡(Dead)
以如下3种方式结束线程
run()
或call()
方法执行完成,线程正常结束;- 线程抛出一个未捕获的
Exception
或Error
; - 直接调用该线程的
stop()
方法来结束该线程(该方法易造成死锁,不推荐使用)
注意:
- 当抛出一个异常后程序会结束,所以线程会终止;
- sleep()方法会阻塞一个线程并不会终止;
- 创建一个新的线程也不会终止另一个线程。
判断线程是否死亡
可以通过isAlive()
方法,线程对象的isAlive()
方法返回true,即为线程存活;返回false,即为线程死亡。
线程处于就绪、运行、阻塞状态时,isAlive()
返回true
;线程处于新建、死亡状态时,isAlive()
返回false
。
start()和run()方法详解
start()和run()介绍
当程序使用new关键字
创建了一个线程后,该线程就处于新建状态,此时它和其他Java对象是一样的,只是由JVM为其分配内存,并初始化其成员变量的值(此时线程对象没有任何的行为,也不执行线程执行体)。
当线程对象调用了start()
方法后,线程就处于就绪状态,JVM为其创建方法调用栈和程序计数器,处于这个状态中的线程还没有真正的开始运行,只是表示这个线程此时是一个可运行状态。何时能运行?取决于JVM的线程调度器的调度。
处于就绪状态的线程获取CPU执行权限
,开始执行run()
方法的线程执行体,此时线程处于运行状态。(若只有一个CPU,任何时刻只能有一个线程处于运行状态,多线程处理任务时,会给人一种并发错觉,实际是CPU执行速度较快,多线程交织执行任务而已)
start()方法源码
public synchronized void start() {/*** This method is not invoked for the main method thread or "system"* group threads created/set up by the VM. Any new functionality added* to this method in the future may have to also be added to the VM.** A zero status value corresponds to state "NEW".*///若线程不是就绪状态,就抛出异常if (threadStatus != 0)throw new IllegalThreadStateException();/* Notify the group that this thread is about to be started* so that it can be added to the group's list of threads* and the group's unstarted count can be decremented. *///将线程添加到ThreadGroup中group.add(this);boolean started = false;try {//通过start0()方法启动线程start0();//设置线程启动的started标志位started = true;} finally {try {if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {/* do nothing. If start0 threw a Throwable thenit will be passed up the call stack */}}}
start()
实际上通过本地方法start0()
启动线程,会新运行一个线程,新线程会调用run()
方法。
run()方法源码
@Overridepublic void run() {if (target != null) {target.run();}}
target
是Runnable对象
,run()
直接调用Thread线程
的Runnable成员
的run()
方法,并不会新建一个线程。
线程控制方法
sleep()方法
sleep()方法介绍
- sleep(long millis)方法是Thread类的一个静态方法,作用是让当前线程暂停一段时间,并进入阻塞状态。
sleep()方法重载方式
public static native void sleep(long millis) throws InterruptedException
:让当前正在执行的线程暂停millis毫秒,并进入阻塞状态。public static void sleep(long millis, int nanos) throws InterruptedException
:让当前正在执行的线程暂停millis毫秒+nanos毫微秒,并进入阻塞状态。(很少用)
sleep()示例
通常用法就是
//让当前线程睡眠1000毫秒,即暂定1s
Thread.sleep(1000);
yield()方法
yield()方法介绍
yield()
方法让当前正在执行的线程暂停,但不会阻塞线程,只是让线程转入就绪状态。yield()
方法让当前线程暂停,让系统的线程调度重新调度一次,所以会出现当某个线程调用了yield()方法后,线程调度器又重新将它调度出来执行。yield()
方法让当前线程暂停后,只有优先级>=当前线程的处于就绪状态的线程才能获取CPU执行权限。
yield()方法重载
public static native void yield();
:静态方法。
yield()示例
//让当前线程暂停
Thread.yield();
线程优先级
- 每个线程执行都有一定的优先级,优先级高的线程获得CPU执行权限的机会比较大。
- 每个线程默认的优先级与创建它的父线程的优先级相同。所以main线程的优先级一般和自己创建的子线程优先级一样。
- Thread类提供
setPriority(int newPriority)
和getPriority()
方法设置和返回指定线程的优先级。其中setPriority()方法的参数可以是一个整数(1-10之间),也可以是静态常量。
MAX_PRIORITY:值为10.
MIN_PRIORITY:值为1.
NORM_PRIORITY:值为5.
join()方法
join()方法介绍
- Thread类提供
join()方法
让一个线程等待另一个线程完成的方法;就是将指定的线程加入到当前线程,这样两个交替执行的线程就变成了顺序执行的线程,如线程A调用了线程B的join()方法,则线程A会等待线程B执行完毕后才会继续执行自己。 join()
方法由使用线程的程序调用,调用线程调用线程t的t.join()方法后将会被阻塞,直到线程t执行完毕,调用线程才能继续执行。一般就是用于主线程内,等待其他线程执行完毕后,再继续执行main()函数。
join()方法重载方式
public final void join() throws InterruptedException
:等待被join的线程执行完毕。public final synchronized void join(long millis) throws InterruptedException
:等待被join的线程的超时时间为millis毫秒。如果在millis毫秒内被join的线程还未结束执行流程,则调用线程不再等待。public final synchronized void join(long millis, int nanos) throws InterruptedException
:等待被join的线程的时间最长为millis毫秒+nanos毫微秒。(很少用)
join()方法示例
(1)未使用join()方法
代码
public class JoinMethodTest {public static void main(String[] args) {System.out.println("main thread start");Thread thread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("child thread start");try {Thread.sleep(1000);} catch (Exception e) {e.printStackTrace();}System.out.println("child thread finshed");}});thread.start();System.out.println("main thread finshed");}
}
运行结果
main thread start
main thread finshed
child thread start
child thread finshed
可以从运行结果看出,main()主线程日志打印的很快,没有等待子线程打印就结束了。
(2)使用join()方法
代码
public class JoinMethodTest {public static void main(String[] args) {System.out.println("main thread start");Thread thread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("child thread start");try {Thread.sleep(1000);} catch (Exception e) {e.printStackTrace();}System.out.println("child thread finshed");}});thread.start();//加入join()方法等待子线程执行完毕,才执行主线程。try {thread.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("main thread finshed");}
}
运行结果
main thread start
child thread start
child thread finshed
main thread finshed
从运行结果可以看出,main thread finshed
结果是在最后打印的,加入join()方法等待子线程执行完毕,才执行主线程。
6种状态的线程生命周期解释
Q&A
为何启动线程需要用start()方法而不是直接调用run()方法?
- 调用
start()
方法启动线程,系统会将该线程对象的run()
方法当作线程执行体来处理。 - 直接调用线程对象的
run()
方法,该方法会被立即执行,而在run()
方法返回之前其他线程无法并发执行(系统会将线程对象的当作一个普通对象,将run()
方法当作一个普通方法,而不是线程执行体。)
start()方法和run()方法
java Thread中,run方法和start()方法的区别
- 概念:
start()
是启动线程,让线程从新建状态变为就绪状态;线程得到CPU时间片后,执行run()
中的线程执行体; - 调用次数:
start()
只能调用一次;run()
可以重复调用。 - 方法类型:启动线程只能用
start()
,系统会把run()
方法当做线程执行体处理;如果直接调用run()
,系统会把线程对象当作普通对象,此时run()
也是一个普通方法,而不是线程执行体。run()方法只是类的一个普通方法而已,如果直接调用run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码。。 - 源码:
start()
源码中实际上通过本地方法start0()
启动线程,会新运行一个线程,新线程会调用run()
方法;而run()
源码中target
是Runnable对象
,run()
直接调用Thread线程
的Runnable
成员的run()
方法,并不会新建一个线程。 - 多线程:用 start方法来启动线程,是真正实现了多线程, 通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法。但要注意的是,此时无需等待run()方法执行完毕,即可继续执行下面的代码。所以run()方法并没有实现多线程。
sleep()和yield()方法的区别
- 依赖线程优先级:sleep()方法暂停当前线程后,会给其他线程执行机会,而不在乎其他线程的优先级;
yield()方法暂停当前线程后,只会给优先级相同或更高的线程执行机会。 - 线程转入状态:sleep()方法将线程转入阻塞状态,知道经过阻塞时间才会转入就绪状态;
yield()方法不会将线程转入阻塞状态,而是将线程转入就绪状态。 - 异常声明:sleep()方法声明抛出了InterruptedException异常;
yield()方法未声明抛出异常。 - 可移植性: sleep()方法的移植性比yield()方法好,所以一般使用sleep()方法控制并发编程。
Java—线程的生命周期及线程控制方法详解相关推荐
- Spring生命周期Bean初始化过程详解
Spring生命周期Bean初始化过程详解 Spring 容器初始化 Spring Bean初始化 BeanFactory和FactoryBean 源码分析 Bean的实例化 preInstantia ...
- Android复习10【Service与Thread的区别、Service的生命周期、Service生命周期解析(相关方法详解、启动方式的不同、绑定)、音乐播放器+服务】
音乐播放器Android代码下载:https://wws.lanzous.com/ifqzihaxvij 目 录 Service与Thread的区别 Service的生命周期 Service生命周 ...
- 线程(线程基本概念、java实现多线程、使用多线程、线程的生命周期、线程同步、线程死锁)
(一)线程基本概念 一. 程序, 进程, 线程的概念 程序: 使用某种语言编写一组指令(代码)的集合,静态的 进程: 运行的程序,表示程序一次完整的执行, 当程序运行完成, 进程也就结束了 个人电脑: ...
- java 多线程编程(包括创建线程的三种方式、线程的生命周期、线程的调度策略、线程同步、线程通信、线程池、死锁等)
1 多线程的基础知识 1.1 单核CPU和多核CPU 单核CPU,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务.微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那 ...
- Java线程的生命周期及线程的几种状态
线程也具有生命周期,主要包括 7 种状态: 出生状态. 就绪状态. 运行状态. 等待状态. 休眠状态. 阻塞状态. 死亡状态, 下面对线程生命周期中的 7 种状态做说明. 出生状态:用户在创建线程时所 ...
- 线程的生命周期 和 线程的通讯
线程的生命周期 概述: 线程从创建到执行完毕的过程就叫做生命周期,整个周期中会不同的状态,状态之间会发生相互转换的现象. 状态罗列: 新建:线程创建出来但是没有开启 就绪:start方法开启了线程, ...
- 线程的生命周期?线程有几种状态
1.线程通常有五种状态:创建,就绪,运行.阻塞和死亡状态. 2.阻塞的情况又分为三种: (1).等待阻塞:运行的线程执行wait方法,该线程会释放占用的所有资源,JVM会把该线程放入"等待 ...
- 线程的生命周期_[线程设计]通过观察者模式监控线程生命周期
推荐书籍:<Java高并发编程详解> 一.无法掌控 Thread 的生命周期 在 Java 中线程的实现只有一个,就是 Thread . 下面是启动一个线程的通用代码,如下: 通过 new ...
- 【微信小程序企业级开发教程】生命周期和app对象详解
文章目录 1 生命周期 2 app对象使用 1 生命周期 2 app对象使用
最新文章
- emacs参考资料整理
- #include 和#include“”的区别
- bash: /etc/apt/sources.list: 权限不够的解决办法
- oracle触发器不允许修改数据库,Oracle数据库使用触发器记录表数据修改记录
- mysql 左连接b表的一条数据_阿里java架构师教你怎么用mysql怒怼面试官
- 服务器无法在发送 http 标头之后设置内容类型。_python socket编程预知内容
- webpack4.0各个击破(9)—— karma篇
- 经过一晚上的浓缩之后
- SPI通信实验---verilog(FPGA作为从机,使用可读可写)
- MySQL 数据库性能调优
- 最牛逼的PHP视频教程115网盘免费下载,嗷嗷给力
- 数字图像处理:对数变换
- 大数据安全体系介绍之技术体系篇
- 浅谈凸优化中的共轭函数
- 三维视频融合 倾斜摄影模型 开放c++源代码 支持与java js交互 内嵌web容器 可用于雪亮工程 等 安防项目 稳定流畅 点卯系列
- 手机APP爬虫技巧(Fiddler+雷电模拟器进行APP抓包)
- Java 学习情况总结
- Cobalt Strike Beacon 初探
- 如何学习自动化测试?一文4从个方面带你入门自动化测试【建议收藏】
- python 类型标注-typing --- 类型标注支持 — Python 3.7.9 文档
热门文章
- C++:函数指针,回调函数
- 为什么要使用 zero_grad()?
- 嵌入式wifi 芯片 推荐_人工智能对汽车芯片设计的影响
- 解决ubuntu18.04中python兼容以及pip兼容问题
- svg与png/jpg快速转换
- 全新精美UI口红机闯三关赢礼品源码
- 原生安卓苹果APP-java抢单派单系统平台源码
- 途观l怎么使用_官宣!中型SUV质量最新排名出炉:汉兰达失前三,大众途观L上榜!...
- 浏览器复制网页链接到微信,显示却只有网页title解决
- vue 刷新当前页面、组件;provide、inject