刚开始学线程的时候也是被这几个方法搞的云里雾里的,尤其是一开始看的毕老师的视频,老师一直在强调执行权和执行资格,看的有点懵逼,当然不是说毕老师讲的不好,就是自己有点没听明白,后来复习看了一些其他的博客总结一下线程中的几个容易懵逼的方法以及线程的六种状态。
先来看线程的6种状态,看下面这张图,这张图是在别人博客里面看见的,但是我感觉他的原图有些不完美所以自己重新画了一张图:
先来解释一下这张图,红色字体表示java中的线程的6种状态。当然图也不是很完善,还有LockSupport对象的park/parkNanos/parkUntil方法没有加入进来。
看着这张图,我们再来说说线程中的几个常用方法:
wait方法:在执行wait方法后,当前线程立即释放锁,wait下面的代码不会再执行了。
sleep方法:而线程sleep之后依然持有锁,我睡了你也别想运行。
yield方法:使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里随机选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的。也就是从Running变为Ready,一直在Runnable里面。
join:在B线程里面写threadA.join( ),B挂起,让A运行,看起来B很礼貌让A运行了,但其实B很虚伪,让别人运行又不释放锁。
notify:在执行notify方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait状态所在的线程才可以获取该对象锁。
以上大部分知识点都是看的别人博客的或者书里面的,自己做了个总结而已。
其实上面这些东西也不用自己死记,自己写两个线程试试就懂了。我一开始也是为了面试死记硬背,发现老是搞混,后来发现还是得手动敲代码,这样记忆才更加深刻。
说完了上面这些容易让人混淆的方法之后,还有两个容易让人混淆的方法那就是interrupt/interrupted/isInterrupted方法,接下来说说这三个方法,这三个方法都是Thread类中的方法,这三个方法表面看上去很相近,但其实功能差很多。
首先是interrupt方法,中文意思是中断暂停的意思,但千万不要被中文意思所误导,他不能使线程中断或者暂停,我们可以简单的将它理解为一个flag,一开始这个flag默认为false,当我们调用interrupt方法之后这个flag从false变为true,对于正常运行的线程来说没有一点点影响,线程还是照常运行,但是对于处于wait/sleep/join状态中的线程来说,这个变化对于线程影响很大。这个后面再详细说明。
其次是interrupted方法,它的返回值是一个Boolean类型的参数,这个方法的作用是返回当前线程的flag值并且将它重置为false。
最后是isinterrupted方法,它的返回值也是一个Boolean类型的参数,这个方法的作用是仅仅返回当前flag的值。
再来说说为什么interrupt方法对于正常运行的线程来说为什么没有影响而对于处于那三种状态的线程有很大的影响呢,如果我们想用interrupt这个flag来停止线程,那必须得我们自己手动写点什么,例如在run方法中while(Thread.currentThread().isInterrupted),当while循环检测到这个flag变为true之后就会跳出循环从而结束线程。那为什么对于那三种状态的线程来说interrupt方法对他们影响很大呢?
以下内容引自这篇博客:
一.对于wait中等待notify/notifyAll唤醒的线程,其实这个线程已经"暂停"执行,因为它正在某一对象的休息室中,这时如果它的中断状态被改变,那么它就会抛出异常。这个InterruptedException异常不是线程抛出的,而是wait方法,也就是对象的wait方法内部会不断检查在此对象上休息的线程的状态,如果发现哪个线程的状态被置为已中断,则会抛出InterruptedException,意思就是这个线程不能再等待了,其意义就等同于唤醒它了.这里唯一的区别是,被notify/All唤醒的线程会继续执行wait下面的语句,而在wait中被中断的线程则将控制权交给了catch语句.一些正常的逻辑要被放到catch中来运行.但有时这是唯一手段,比如一个线程a在某一对象b的wait中等待唤醒,其它线程必须获取到对象b的监视锁才能调用b.notify()[All],否则你就无法唤醒线程a,但在任何线程中可以无条件地调用a.interrupt();来达到这个目的.只是唤醒后的逻辑你要放在catch中,当然同notify/All一样,继续执行a线程的条件还是要等拿到b对象的监视锁。