线程的状态转换图
线程状态类型
新建状态(New)
用new语句创建的线程对象处于新建状态,此时它和其他Java对象一样,仅仅在堆区被分配了内存。
就绪状态(Runnable)
当一个线程对象创建后,其他线程调用它的start()方法,该线程就进入就绪状态,Java虚拟机会为它创建方法调用栈和程序计数器。处于这个状态的线程位于可运行池中,等待获得CPU的使用权。
运行状态(Running)
处于这个状态的线程占用CPU,执行程序代码。在并发编程环境中,如果计算机只有一个CPU,那么任何时刻只会有一个线程处于这个状态。如果计算机有多个CPU,那么同一时刻可以让几个线程占用不同的CPU,使它们都处于运行状态。只有处于就绪状态的线程才有机会转到运行状态。
阻塞状态(Blocked)
阻塞状态是指线程因为某些原因放弃CPU,暂时停止线程。当线程处于阻塞状态时,Java虚拟机不会给线程分配CPU,直到线程重新进入就绪状态,它才有机会转到运行状态。
阻塞状态可以分为以下三种:
等待阻塞:位于对象等待池中的阻塞状态(Blocked in object's wait pool),当线程处于运行状态时,如果执行了某个对象的wait()方法,Java虚拟机就会把线程放到这个对象的等待池中。
同步阻塞:位于对象锁池中的等待状态(Blocked in object's lock pool),当线程处于运行状态,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,Java就会把这个线程放到这个对象的锁池中。
其他阻塞:当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出了I/O请求,就会进入这个状态。
死亡状态(Dead)
当线程退出run()方法时,就进入死亡状态,该线程结束生命周期。线程有可能是正常执行完run()方法而退出,也有可能是遇到异常而退出
在《JAVA并发编程的艺术》一书中线程的状态如下:
注意: 同步阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态,阻塞在java.concurrent包中Lock接口的线程状态却是等待阻塞状态,因为java.concurrent包中Lock接口对于阻塞的实现均使用了LockSupport类中的相关方法。
线程调度的方法
Thread.sleep()方法
public static native void sleep(long millis)是Thread的静态方法,它是让当前线程按照指定的时间休眠,其休眠时间的精度取决于处理器的计时器和调度器。需要注意的是如果当前线程获得了锁,sleep方法并不会失去锁。sleep方法经常拿来与Object.wait()方法进行比价,这也是面试经常被问的地方。
sleep() VS wait()
两者主要的区别:
- sleep()方法是Thread的静态方法,而wait是Object实例方法
- wait()方法必须要在同步方法或者同步块中调用,也就是必须已经获得对象锁。而sleep()方法没有这个限制可以在任何地方种使用。另外,wait()方法会释放占有的对象锁,使得该线程进入等待池中,等待下一次获取资源。而sleep()方法只是会让出CPU并不会释放掉对象锁;
- sleep()方法在休眠时间达到后如果再次获得CPU时间片就会继续执行,而wait()方法必须等待Object.notift/Object.notifyAll通知后,才会离开等待池,并且再次获得CPU时间片才会继续执行。
Thread.yield()方法
public static native void yield()也是Thread的静态方法。如果此时具有相同优先级的其他线程处于就绪状态,那么yield()方法将当前运行的线程放到运行池中,并从线程池中任意挑选一个线程运行。如果没有相同优先级的可运行线程,则yield()方法什么也不做。需要注意的是,让出的CPU并不是代表当前线程不再运行了,如果在下一次竞争中,又获得了CPU时间片当前线程依然会继续运行。
join()方法
当前线程可以调用另一个线程的join()方法,可以看做是线程间协作的一种方式,当前运行的线程将转到阻塞状态,直至另一个线程结束,他才会恢复运行
参考文献
《Java面向对象编程》
《JAVA并发编程的艺术》