背景
先来探讨一个关于多线程的基础知识:java线程有多少种状态?根据JDK定义,答案是六种!为什么很多人给出的答案却是五种呢?这极有可能是将操作系统层面的线程状态和java线程状态混为一潭了。因为,小编在翻阅JDK源码的基础上,介绍一下java线程的六种状态以及操作系统层面的五种状态。
java线程状态
JDK中声明了六种java线程状态,以枚举的形式定义在Thread.State中,而且注释开篇撇清了和操作系统层面线程状态的关系-----【这些是状态是虚拟机状态,不反映任何操作系统的线程状态】,英文原文描述如下:
简单来介绍一下这六种状态。
1.new:新建状态,新创建一个线程对象时的初始状态,此时尚未调用start()方法。
2.runnable:就绪状态,Java线程把操作系统中的就绪和运行两种状态统一称为“就绪状态或者可运行状态”。英文相关描述如下:
A thread in the runable state is executing in the Java virtual machine but it may be waiting for other resources from the operation system such as processor.
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu的使用权。
3.blocked: 阻塞状态,表示线程进入等待状态,也就是线程因为某种原因放弃了CPU使用权,阻塞也分为几种情况:
- 等待阻塞:运行的线程执行了Thread.sleep,Object.wait(),join()等方法,JVM会把当前线程设置为等待状态,当sleep结束,join线程终止或者线程被唤醒后,该线程从等待状态进入阻塞状态,重新抢占锁后进行线程恢复。
- 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被其他线程占用了,那么JVM会把当前的线程放入到锁池中。
- 其他阻塞:发出了I/O请求时,JVM会把当前线程设置为阻塞状态,当I/O处理完毕则线程恢复。
4. waiting: 等待时间,没有超过时间,要被其他线程或者有其他的中断操作;即一个正在无限期等待另一个线程执行一个特别的动作的线程处于waiting状态,英文原文如下:
A thread that is waiting indefinitely for another thread to perform a particular action is in the state.
一个线程进入waiting 状态是因为调用了方法Object.wait(),Thread.join()或者LockSupport.park()。然后会等其他线程执行一个特别的动作,比如:
- 一个调用了Thread.join方法的线程会等待指定的线程结束。
- 一个调用了某个对象的wait方法的线程会等待另一个线程调用此对象的notify()或notifyAll()。
5.Time_waiting:等待超时,超过以后会自动返回;如下方执行超时,就会进入超时等待状态:Thread.sleep(long),Object.wait(long),Thread.join(long),LockSupport.park(long),LockSupport.parkNanos(long),LockSupport.parkUntil(long)。
6. Terminated:终止状态,表示当前线程执行完毕。
我们可以通过getState()来查看线程的当前状态:
线程状态间的转换
借一个图来描述:
关于具体的转换场景,图中描述的比较清楚,此处不在赘述。注意:
- sleep,join,yieId时并不释放对象锁资源,而执行函数wait()时会释放锁,对象在被notif/notifyAll唤醒时,重新去抢夺获取对象锁资源。
- sleep可以在任何地方使用,而wait,notify,notifyAll只能在同步方法或者同步块中使用。
- 调用obj.wait()会立即释放锁,以便其他线程可以执行notify,但notify()不会立刻释放sycronized(obj)中的对象锁,必须要等notify()所在线程执行完同步方法或者同步块才会释放这把锁,然后供线程等待池的线程来抢夺对象锁。 wait方法是Object的方法,线程释放锁,进入waiting或timed_waitind 状态,等待时间到了或被 notif/notifyAll唤醒后回去竞争锁,如果获得锁,进入runnable,否则进入阻塞状态等待获取锁。
操作系统层面线程状态
很多人会把操作系统层面的线程状态与java线程状态混淆,所以导致有的文章中把java线程状态写成5种,在此我们说清楚一个问题,java线程状态是6个,操作系统层的线程是5种,如下图所示:
下面分别介绍一下这五种状态:
- new :一个新的线程被创建,等待该线程被调用执行。
- ready:表示线程已被创建,正在等待系统调度分配CPU使用权或者时间片已用完,此线程被强制暂停,等待下一个属于它的时间片到来。
- running:表示线程获得了CPU使用权,正在占用时间片。
- waiting:表示线程等待(或者说挂起),等待某一事件(如IO或另一个线程)执行完,让出CPU资源给其他线程使用。
- terminated:一个线程完成任务或者其他终止条件发生,该线程终止进入退出状态,退出状态释放该线程所分配的资源。
需要注意的是:操作系统中的线程除去new和terminated状态,一个线程真实存在的状态是ready,running和waiting。
Thread.State中的Runnable状态涵盖了操作系统层面的【可运行状态】,【运行状态】和【阻塞状态】(由于BIO导致的线程阻塞,在java里无法区分,仍然认为是可运行,就好比我们在run()方法里调用IO方法,再者虽然有线程上下文切换但在JAVA层面还是运行的)。
至此,我们就把java线程状态以及操作系统层面的线程就理清了,喜欢的小伙伴可以关注我呀