操作系统线程状态:
- NEW(新建):在OS层面线程已被创建,如通过new Thread(runnable)创建一个线程;但是还不允许分配CPU执行。
- RUNNABLE / READY(就绪):OS线程已被创建,随时可以被CPU调度启用。
- RUNNING(运行):当有空闲CPU时,OS会将其分配给一个RUNNABLE状态的线程。
- BLOCKED / WAITING(阻塞/休眠/等待/挂起):阻塞API或等待某个事件(如条件变量),线程就会切换到阻塞状态,同时释放CPU使用。该状态在 Java中被划分为了 BLOCKED,WAITING,TIMED_WAITING 三种状态。当线程调用阻塞式 API时,进程(线程)进入等待状态,这里指的是操作系统层面的。从 JVM层面来说,Java线程仍然处于 RUNNABLE 状态。
- TERMINATED(终止):线程执行完成,被强行终止或出现异常,就进入终止状态,线程的生命周期结束。
Java线程状态:
- NEW(新建):Java调用new Thread(),就新建了一个线程。
- RUNNABLE(运行):调用线程的start()方法创建,因为Java中的Thread是应用层面的API,无法直接感知到内核对线程的操作,因此将操作系统线程的READY和RUNNING统一归到此处的运行状态,即Java中的线程处于RUNNABLE状态时,可能正在执行,也可能没有正在执行,而是在等待被分配CPU资源。(比如RUNNABLE状态的线程的任务运行到一半,执行该线程的CPU被调度去做其他事情,导致该线程暂时不执行,但它的状态依然保持RUNNABLE不变,因为它随时可能被调度回来继续执行任务)
- BLOCKED(阻塞):由进入synchronized时未获取到监视器锁导致从RUNNABLE进入该状态,或在WAITING / TIMED_WAITING状态下唤醒也可能进入该状态。即正在阻塞等待一个监视器锁(锁对象)的线程处于这一状态(阻塞队列)。
- WAITING(等待):当调用了没有设置超时时间的wait()方法或遇到join()方法或LockSupport的park()方法时(如ReentrantLock未获取到锁,即进入WAITING状态) 从RUNNABLE进入该状态。处于该状态的线程不会被分配CPU执行时间,且要等待被显式地唤醒。即正在无限等待另一个线程执行一个唤醒动作的线程处于这一状态(等待队列)。与阻塞状态区别:线程在没有拿到锁时是BLOKCED状态,拿到锁后的执行期间,如果因为达不到某些条件,想要暂停等待一会,先不执行,此时调用wait()可以进入WAITING状态,进入WAITING后即释放锁资源,并等待通知机制来唤醒当前线程,唤醒后亦需要重新竞争锁资源。
- TIMED_WAITING(计时等待):当调用了设置时间参数的sleep() / wait() / join() / parkNanos() / parkUntil()方法时从RUNNABLE进入该状态。处于该状态的线程不会被分配CPU执行时间,在达到一定时间后它们会自动唤醒。
- TERMINATED(终止)
几种常见方法:
- Thread.yield():当前线程调用此方法,即放弃获取的CPU时间片,但不释放锁资源,由OS的运行状态变为OS的就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
- Thread.sleep(long millis):当前线程调用此方法,即进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动唤醒进入就绪状态。
- obj.wait():当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) 的超时时间到自动唤醒。 与sleep()方法比较:sleep()是让当前线程休眠,不涉及到对象类,也不需要获得对象的锁,所以是线程类的方法。wait() 是让获得对象锁的线程实现等待,前提是要楚获得对象的锁,所以是类的方法,且使用wait()方法必须放在 synchronized 块里面。
- obj.notify():唤醒在此对象监视器上等待的任意单个线程,notifyAll()唤醒在此对象监视器上等待的所有线程。在调用notify()或者notifyAll()后,调用wait()的等待线程不会立刻从等待队列返回,而是从等待队列移动到同步队列,准备竞争对象监视器,即线程状态从WAITING转化为BLOCK。(wait() / notify() / notifyAll()必须配合监视器一起使用,park()和unpark()则不必;unpark()通过传参可以指定要唤醒的线程且unpark()可以在park()执行前就调用一样有效)。
- t.join():当前线程里调用其它线程t的join方法,当前线程进入WAITING / TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程一般情况下进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join()基于wait()实现)。