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

面试题-线程从创建到消亡经历了哪些阶段?

时间:2023-03-20 02:12:57 科技观察

今天,我们再结合操作系统线程和编程语言线程来讨论线程的生命周期。线程的生命周期其实并没有我们想象的那么简单!!理解了线程的生命周期,本质上就是理解了生命周期的各个方面。节点的状态转换机制很好。接下来,我们将分别详细描述一般线程生命周期和Java语言线程生命周期。通用线程生命周期通用线程的生命周期一般可以分为五个状态:初始状态、可运行状态、运行状态、休眠状态和终止状态。我们可以简单的用下图来表示这五种状态。初始状态线程已创建,但不允许分配CPU执行。需要注意的是,这种状态是编程语言所特有的。这里所说的线程已经创建了,只是说在编程语言中已经创建好了。在操作系统中,并没有创建真正的线程。Runnable状态的线程可以分配CPU执行。至此,操作系统中的线程创建成功,可以分配CPU执行了。运行状态当操作系统中有一个空闲的CPU时,操作系统会将这个空闲的CPU分配给一个处于可运行状态的线程,分配给该CPU的线程的状态将转换为处于运行状态的线程调用,sleepstate,andrunningstate一个阻塞的API(例如,以阻塞的方式读取一个文件)或者等待一个事件(例如,等待一个条件变量等),线程的状态会转换到睡眠状态。这时候线程会释放CPU资源,处于休眠状态的线程没有机会获得CPU的使用权。一旦等待条件发生,线程就从休眠状态转换到可运行状态。终止态线程在执行完或者发生异常后都会进入终止态,处于终止态的线程不会再切换到其他任何状态,这也意味着线程的生命周期结束了。以上就是一般的线程生命周期。接下来,我们来看看Java语言中的线程生命周期。Java中的线程生命周期Java中的线程生命周期一共可以分为六种状态:初始化状态(NEW)、可运行/运行状态(RUNNABLE)、阻塞状态(BLOCKED)、无限等待状态(WAITING)、活动状态Time限制等待状态(TIMED_WAITING)、终止状态(TERMINATED)。需要了解的是,虽然Java语言中线程的状态有很多种,但实际上在操作系统层面,线程中的阻塞态(BLOCKED)、无限等待态(WAITING)、定时等待态(TIMED_WAITING)Java线程是一种状态,即一般线程生命周期中的休眠状态。也就是说,只要Java中的线程处于这三种状态,那么这个线程就没有CPU的使用权。了解了这些之后,我们就可以用下图来简单的表示一下Java中线程的生命周期。我们也可以理解为阻塞状态(BLOCKED)、无限等待状态(WAITING)、定时等待状态(TIMED_WAITING),这就是线程休眠的三种原因!接下来,让我们看看Java线程中的状态。它是如何转换的。RUNNABLE和BLOCKED之间的状态转换只有一种场景会触发这种转换,那就是线程等待synchronized隐式锁。同步修饰的方法和代码块只允许一个线程同时执行,其他线程需要等待。此时,等待线程将从RUNNABLE状态转换到BLOCKED状态。当等待线程获取到synchronized隐式锁时,它会再次从BLOCKED状态转换到RUNNABLE状态。在这里,大家需要注意的是:当一个线程调用阻塞API时,在操作系统层面,该线程会切换到休眠状态。但是在JVM中,Java线程的状态是不会改变的,即Java线程的状态仍然是RUNNABLE状态。JVM不关心与操作系统调度相关的状态。从JVM的角度来看,等待CPU使用权(操作系统中的线程处于可执行状态)和等待IO操作(操作系统中的线程处于休眠状态)没有区别。它正在等待某种资源,因此被归类为RUNNABLE状态。我们通常说的Java调用阻塞API时,线程会阻塞,这是指操作系统线程的状态,而不是Java线程的状态。RUNNABLE和WAITING状态转换一个线程从RUNNABLE状态转换到WAITING状态一般有三种场景。场景一获取synchronized隐式锁的线程调用无参数的Object.wait()方法。这时候线程就会从RUNNABLE状态转变为WAITING状态。场景2调用不带参数的Thread.join()方法。join()方法是一种线程同步方法。比如在threadA线程中调用threadB线程的join()方法,threadA线程会等待threadB线程执行完毕。当threadA线程等待threadB线程执行时,它的状态将从RUNNABLE转变为WAITING。当threadB执行完毕后,threadA线程的状态就会从WAITING状态变为RUNNABLE状态。场景3调用LockSupport.park()方法,当前线程会被阻塞,线程状态由RUNNABLE变为WAITING。调用LockSupport.unpark(Threadthread)可以唤醒目标线程,目标线程的状态将从WAITING状态变为RUNNABLE。RUNNABLE和TIMED_WAITING的状态转换一般可以分为五种场景。场景1调用带有超时参数的Thread.sleep(longmillis)方法。场景2获取synchronized隐式锁的线程调用Object.wait(longtimeout)参数时带超时参数。场景3使用超时参数调用Thread.join(longmillis)方法。场景4调用带有超时参数的LockSupport.parkNanos(Objectblocker,longdeadline)方法。场景5调用带有超时参数的LockSuppor.parkUntil(longdeadline)方法。从NEW到RUNNABLE状态,Java刚刚创建的Thread对象就是NEW状态。创建Thread对象有两种主要方法。一种是继承Thread对象,重写run()方法;另一种是实现Runnable接口,重写run()方法。注意:这里说的是创建Thread对象的方法,不是创建线程的方法。创建线程的方法包括创建Thread对象的方法。继承Thread对象publicclassChildThreadextendsThread{@Overridepublicvoidrun(){//需要在线程中执行的逻辑}}//创建线程对象ChildThreadchildThread=newChildThread();实现Runnable接口publicclassChildRunnableimplementsRunnable{@Overridepublicvoidrun(){//需要在线程中执行的逻辑}}//创建线程对象ThreadchildThread=newThread(newChildRunnable());处于NEW状态的线程不会被操作系统调度,因此它们不会执行。要在Java中执行一个线程,它需要转换到RUNNABLE状态。从NEW状态切换到RUNNABLE状态,只需要调用线程对象的start()方法即可。//创建一个线程对象ThreadchildThread=newThread(newChildRunnable());//调用start()方法使线程从NEW状态过渡到RUNNABLE状态childThread.start();RUNNABLE到TERMINATED状态的线程执行完run()方法后,或者执行run()方法时抛出异常,就会被终止,处于TERMINATED状态。如果我们需要中断run()方法,我们可以调用interrupt()方法。