当前位置: 首页 > 科技观察

面试官没想到我一个Java线程生命周期能讲半个小时

时间:2023-03-20 16:15:53 科技观察

转载请联系Java极客技术公众号。面试官:你不是精通Java并发吗?让我们从基本的Java线程生命周期开始。好的,面试官。啦啦啦啦...如果要说Java线程的生命周期,那我觉得应该先说说操作系统的线程生命周期,因为JVM是运行在操作系统上的,绕不过去,并且可以说Java语言中的线程本质上就是操作系统的线程。聪明的你一定发现了,不管是操作系统,Java还是C#都有线程的概念。它们之间,在线程的生命周期上肯定有相似之处。否则的话,操作系统有自己的生命周期过程,Java有自己的一套,C#也有自己的一套,而且他们必须能够相互配合。这样的代价,想想也太大了吧?那么我们来看看,一般的线程生命周期是怎样的?Line?):可以看到,主要有5种状态:new、ready、running、waiting、terminated。其中:new只是表示已经创建了这个线程,但是不允许分配CPU执行。因为这个状态只代表你在编程语言层面已经创建了,而操作系统层面还没有创建,所以肯定谈不上分配CPU去执行就绪状态。这意味着你已经在操作系统层面创建成功,那么下一步就是等待分配的CPU执行。你还记得那句经典的话吗?预备,开始!运行状态,相信你会知道的,我已经准备好了,如果我这时候分配一个CPU,我能走吗?那不是运行状态吗?嗯,等待状态就是当线程处于运行状态的时候,突然发现,哎,我需要进行一个I/O操作,或者需要等待某个事件的发生(比如,某个条件变量是必需的),这时候不可以吗?继续快乐奔跑。那么该怎么办?等一会,那你已经在等了,占用的CPU资源是不是该释放了?所以,处于等待状态的线程永远没有机会获得CPU的使用权。你听说过“永远没有机会”这句话吗?被三言两语吓坏了,我肯定永远没有机会执行了。别担心,你不是在等待。当你的等待事件发生时,你可以继续运行状态。当整个线程执行完毕,或者发生异常时,进入终止状态,即线程的使命完成。处于终止状态的线程不会切换到其他状态。大致的线程生命周期以及如何在它们之间切换到这里应该就比较清楚了。接下来,我们来看一下Java线程的生命周期。在此基础上如何优化呢,有什么区别呢?我们先来看看Java线程的生命周期。我们看一下源码定义的状态(为了突出重点,我去掉了所有的注释):publicenumState{NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;}可以清楚的看到源码中定义了6种线程状态。刚才有几种常见的状态?5种,对,现在有6种。这6个状态是干什么用的?刚才的5个状态,以及它们之间的切换,我都弄清楚了。这6种状态是如何切换的呢?别着急,阿粉这么贴心,一定是画好了图:下面分别来看这6个状态:NEW到RUNNABLE,应该挺容易理解的,就是线程调用了start方法。Java刚刚创建的Thread对象是NEW状态。创建Thread对象主要有两步,一是继承Thread对象,重写run()方法,二是实现Runnable接口,重写run()方法,将实现类作为参数创建Thread对象但是记住,NEW只是说,这个线程是在编程语言层面创建的,而在操作系统层面还没有创建,所以当然不会被操作系统调度,更不用说执行了。所以如果一个Java线程要执行,就必须转换为RUNNABLE状态,即线程调用start方法RUNNABLE和BLOCKED。如果线程等待synchronized隐式锁,就会从RUNNABLE状态转为BLOCKED状态。因为synchronized修饰的方法/代码块只允许一个线程同时执行,所以其他线程只能等待。当等待线程获得synchronized隐式锁后,会从BLOCKED状态变为RUNNABLE状态。这里有什么吗?一个问题?即当一个线程的条件发生wait时,线程在操作系统层面会进入waiting状态,那么在JVM层面呢?在JVM级别,Java线程状态不会改变。即此时Java线程的状态还处于RUNNABLE状态。RUNNABLE和WAITING状态转换。感觉图已经解释的很好了。RUNNABLE和TIMED_WAITING的状态转换在这里不再赘述。感觉图已经解释的很好了。我不会在这里详细介绍。仔细观察后,你会发现TIMED_WAITING与WAITING相比,超时参数更多。毕竟,TIMED_WAITING是从RUNNABLE到TERMINATED的限时等待。这个过程比较容易理解。线程执行完run()方法后,会自动进入TERMINATED状态。当然,如果在run()方法执行过程中抛出异常,也会导致线程终止。有时我们可能需要强行中断run()方法的执行。我们应该做什么?使用stop()方法还是interrupt()方法?正确的姿势是调用interrupt()方法stop()方法会真正杀死线程,不给线程喘息的机会,如果被杀死的线程持有同步隐式锁,那么锁就再也不会释放了,并且下一个线程将无法获得同步隐式锁。是不是特别危险?同样,suspend()和resume()不推荐使用interrupt()方法比stop()方法温和得多。它只是通知线程不需要执行后续操作。线程可以选择现在不执行它。当然它也可以选择执行一会再停止,或者我就是不听你的也没关系,如果你非要执行完,interrupt()只是通知你而已。比如你想坐火车去一个地方,突然通知你火车晚点了。你可以选择无视通知继续等待,也可以选择其他高铁,但无论你做什么,都与火车站无关。它通知你在Java线程生命周期中,RUNNABLE状态是就绪状态和运行状态的组合,而BLOCKED、WAITING、TIMED_WAITING这三种状态实际上是等待状态。也就是说,线程必须等待某些事件发生,才能继续执行。关于Java线程的生命周期,就到此为止吧。画图+解释,和面试官聊半小时应该没问题吧?