Java线程的6种状态与相关方法

image.png

Java线状态详细说明

1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。

2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。

线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中。此时虽然尚未获得CPU的使用权,但已处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。

3. 阻塞(BLOCKED):表示线程阻塞于重量级锁。

4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。

5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。

6. 终止(TERMINATED):表示该线程已经执行完毕。

Java线程有哪些方法

下面讲的这些方法都可以配合上图一起看。理解它在线程中发挥的作用。

run()与start()

Thread类是Java里对线程概念的抽象,可以这样理解:我们通过new Threa()其实只是new出了一个Thread的实例,还没有和操作系统中真正的线程挂钩起来。只有执行了start()方法后,才实现了真正意义上的启动线程。

Start()方法让一个线程进入就绪队列等待分配cpu,分到cpu后才调用实现的run()方法,start()方法不能重复调用,如果重复调用会抛出异常。

而run方法是业务逻辑实现的地方,本质上和任意一个类的任意一个成员方法并没有任何区别,可以重复执行,也可以被单独调用。

yield(礼让线程)

未使用礼让线程的案例

图片24.png

图片25.png

使用礼让线程的案例

图片26.png

图片27.png

yield()方法,使当前线程让出cpu占有权。但让出的时间是不可设定的。也不会释放锁资源。注意:并不是每个线程都需要锁的,而且调用yield()的位置未必就持有锁。我们完全可以等锁释放掉后再调用yield方法。

yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还可能被线程调度程序再次选中。

结论:yield()不是让线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转为就绪状态,但可能没有效果,只能说把自己的时间片尽量缩短,给别的线程提供机会。

Join(加入线程)(此处为常见考点)

把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行。比如在线程B中调用了线程A的Join方法,直到线程A执行完毕后,才会继续执行线程B。

很多新手在学习join线程时总是理解的偏差。

比如:在主线程中,准备开启两个新的线程。现在想让这两个线程具有优先级顺序,他们就会这样实现:

image.png

当代码运行时,发现两个线程同时执行了。于是对这个问题百思不得其解。事实上,可能你还没有完全理解join方法的原理。

我们称join方法味加入线程。加入加入,参照是谁呢?没错,参照物正是其当前所在的主线程。而不是你的同级线程。

image.png

当我们将代码改成这个样子后,thread和thread2这两个线程具有了优先级顺序。而它能做到让现成之间有顺序的原因是:当thread启动后,我们进行了join加入操作。此时我们的main线程会进入等待唤醒状态。直到thread线程执行完后,main线程继续执行。此时thread2线程才会开始启动,从而继续进行join加入操作。此时的mian线程也会进入等待唤醒状态,直到thread2也执行完毕后,才结束自己的生命周期。

从源码理解join

在介绍刚才的案例时,我多次提到,join方法的调用,会使当前线程进入等待唤醒状态。

诶?凭什么是等待唤醒状态。为啥不是阻塞呢?

我们进入join方法的源码。

image.png

我们发现join方法里面调用了join(0);于是继续点击去看。

image.png

好家伙,一眼就看到一个synchronized,你跟我说是等待唤醒?

那不好意思了,能走到这一步代表你已经获取到了main函数所在类的类锁了。

image.png

因此我们实际让线程真正进入等待的,是wait(long)方法。

诶?听博主这么一说确实对线程状态的概念理解的更清晰了。不过咋这么想揍他呢?稍等,大侠我们的问题还没解决完呢。

我们记得当时调用join方法时,实际上调用的是join(0)这个方法。所以程序就势必会走到下面这一步。

image.png

那么问题就是。此处调用wait方法将main线程进入等待,但是什么时候进行唤醒呢?

在我找了半天程序,就是没有看到在哪里进行的唤醒。最后自己想了想是不是在线程销毁的时候,去看了下Thread类有没有重写finalize()方法,但是可惜不是这里。而且也不能是这里。因为执行finalize()方法的线程是一个低优先级的线程,他虽然一定会被执行,但是这段等待时间在多线程环境下一定是致命的。

网上找了下资料,果然大佬还是多。找到了答案。由于本人这里暂时没有jvm源码,也就copy了下这位大神的东西了。还是在线程执行完毕,不过这次是jvm底层会去做一些校验

image.png

jvm在关闭线程之前会检测其所处的父级线程,是否因为自己所等待。然后执行notfyAll(),这样父级就被唤醒了。

当这一层你也弄清楚了,你一定会恍然大悟,从而放博主一马的。

线程的优先级

在Java线程中,通过一个整型成员变量priority来控制优先级,优先级的范围从1~10,在线程构建的时候可以通过setPriority(int)方法来修改优先级。默认优先级是5,优先级高的线程分配时间片的数量要多于优先级低的线程。

设置线程优先级时,针对频繁阻塞(休眠或者I/O操作)的线程需要设置较高优先级。而偏重计算(需要较多CPU时间或者偏运算)的线程则设置较低的优先级,确保处理器不会被独占。在不同的JVM以及操作系统上,线程规划会存在差异。有些操作系统甚至会忽略对线程优先级的设定。

守护线程

image.png

上面程序肯定主线程比守护线程提前结束,我们来看看运行效果。

image.png

只要主线程执行结束,依附于主线程的守护线程也会结束。因为异步的原因会稍微多跑一点点。

finally一定会被执行么(吊打面试官)

image.png

image.png

总结

Daemon(守护)线程是一种支持性线程,因为它主要被用作程序中后台调度以及支持性工作。这意味着,当一个Java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出。可以通过调用Thread.setDaemon(true)将线程设置为Daemon线程。我们一般用不上,比如垃圾回收线程就是Daemon线程。

Daemon线程被用作完成支持性工作,但是在Java虚拟机退出时Daemon线程中的finally块并不一定会执行。在构建Daemon线程时,不能依靠finally块中的内容来确保执行关闭或者清理资源的逻辑。

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大将黄猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值