- 不那么重要(主要帮助理解线程问题),面试很少遇到
6种线程状态
1. NEW :
2. RUNNABLE :
可运行状态,由线程调度器可以安排执行(线程被调度器选中执行)
底下又分两种小状态:ready、running
yield方法:从running->ready
3. WAITING: .
4. TIMED WAITING:
5. BLOCKED:
被阻塞,正在等待锁.
synchronized上到某个锁上,这个时候还没有拿到锁,需要等待锁—->此时被阻塞
6. TERMINATED:
线程结束
线程状态之间的状态迁移
图解

解释
- 线程被new出来了但没有start时,处于NEW状态
- 线程执行结束处理TERMINATED
- cpu分配时间片给线程执行(分时技术===>时间片、时间片轮转、轮询?)
- 获得时间片的线程处于RUNNING
- 没有获得cpu的处于READY,暂时让出了cpu,等待再次被调度去执行(使用yield方法到达READY)
- 等待进入同步代码的锁synhronized,正在等待某一把锁但是还没有竞争到这把锁,正在抢这把锁的线程处于BLOCKED(阻塞)
- 并没有进入阻塞状态,没有进入阻塞队列,等着被唤醒后才可以执行,而是说进入了一个忙等待的状态(即自旋的等待)的线程处于WAITING状态===>o.wait(); t.join(); LockSupport.park(); Lock.lock();
- 注:等锁的过程除了synchronized这种状态叫做BLOCKED之外,其他都叫WAITING(可以不深究,没什么人会问)
- 隔一段时间之后自动醒过来的线程处于TIMED WAITING状态===>Thread.sleep(time); o.wait(time); t.join(time); LockSupport.parkNanos(); LockSupport.parkUntil();(凡是带时间的就处在TIMED WAITING状态)
- 上图中的线程状态全是由jvm管理的,因为jvm管理这些状态时也要通过操作系统(哪个是jvm,哪个是os,这两个分不开,jvm是跑在os上的普通程序)
- 杀死线程也算terminated
- 线程挂起就是说线程从cpu时间片上移除,进入等待状态?ready?
- 上图中的线程状态与os中的是一一对应的吗?这个要看jvm的实现,以前是一一对应的,现在不好说,但是纤程必然不是一一对应的;正常是一一对应的
- java中ready和running两种状态合并为runnable状态,所以处于runnable状态时可能在就绪队列中等待,也可能正在运行
- os中是进程的概念,linux中的线程是轻量级进程,与进程状态差不多
- 操作系统中挂起的概念是暂时放到外存中去!!!
- 调用哪些方法,线程会释放锁;调用那些方法,线程不会释放锁?
- 不释放锁:
- 线程执行同步代码块或同步方法时,程序调用Thread.sleep(Long l)、Thread.yield()方法暂停当前线程的执行
- 线程执行同步代码块时,其它线程调用该线程suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)===>t.suspend一般由其他线程调用,而不是自己调用
- 尽量避免使用suspend()和resume()来控制线程
- 释放锁:
- 当前线程的同步方法、同步代码块执行结束
- 当前线程的同步方法、同步代码块遇到break、return终止该代码块、该方法的继续执行
- 当前线程的同步方法、同步代码块中出现了未处理Error和Exception,导致异常结束
- 当前线程在同步方法、同步代码块中执行了线程对象的wait()方法,当前线程暂停,并释放锁
SleepHelper帮助类(只是简答地将try……catch……扔到帮助类中)
在演示式的代码中写很多try……catch……,看起来费劲
package com.mashibing.util;import java.util.concurrent.TimeUnit;public class SleepHelper {public static void sleepSeconds(int seconds) {try {TimeUnit.SECONDS.sleep(seconds) ;} catch (InterruptedException e) {e.printStackTrace();}}}
用代码说明状态转化
package com.mashibing.juc.c_000_threadbasic;import com.mashibing.util.SleepHelper;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.LockSupport;import java.util.concurrent.locks.ReentrantLock;/*** title:${file_name}* 关于线程状态的实验** @author 马士兵 http://www.mashibing.com* @version 2.0* @date ${date}*/public class T04_ThreadState {public static void main(String[] args) throws Exception {//1===================================================Thread t1 = new Thread(() -> {System.out.println("2: " + Thread.currentThread().getState());for (int i = 0; i < 3; i++) {SleepHelper.sleepSeconds(1);System.out.print(i + " ");}System.out.println();});System.out.println("1: " + t1.getState());t1.start();// 等待t1线程结束// 在主方法中调用join,就说明主线程要等待t1线程执行结束才能继续向下执行t1.join();System.out.println("3: " + t1.getState());//2===================================================Thread t2 = new Thread(() -> {try {LockSupport.park();System.out.println("t2 go on!");TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}});t2.start();// 睡1秒钟,确保t2开始执行并被阻塞park// 为什么这里不要try……catch……???TimeUnit.SECONDS.sleep(1);System.out.println("4: " + t2.getState());LockSupport.unpark(t2);// 睡1秒钟,确保t2被唤醒// 为什么这里不要try……catch……???TimeUnit.SECONDS.sleep(1);// 为什么会TIMED_WAITING,因为t2醒了之后会sleepSystem.out.println("5: " + t2.getState());//3===================================================final Object o = new Object();Thread t3 = new Thread(() -> {synchronized (o) {System.out.println("t3 得到了锁 o");}});new Thread(() -> {// 线程申请这把锁,却拿不着的时候处于BLOCKED状态!!!synchronized (o) {SleepHelper.sleepSeconds(5);}}).start();SleepHelper.sleepSeconds(1);t3.start();SleepHelper.sleepSeconds(1);System.out.println("6: " + t3.getState());//4===================================================final Lock lock = new ReentrantLock();Thread t4 = new Thread(() -> {lock.lock(); //省略try finally,为了看得清楚,重点看业务逻辑// ===>JUC中的锁===>CAS来实现(忙等待)===>不会进入BLOCKED状态,而是进入WAITING状态// 只有synchronized这样的锁才进入BLOCKED状态System.out.println("t4 得到了锁 o");lock.unlock();});new Thread(() -> {lock.lock();SleepHelper.sleepSeconds(5);lock.unlock();}).start();// 保证上锁完成SleepHelper.sleepSeconds(1);t4.start();SleepHelper.sleepSeconds(1);System.out.println("7: " + t4.getState());//5===================================================Thread t5 = new Thread(() -> {LockSupport.park();});t5.start();SleepHelper.sleepSeconds(1);// 此时t5处于WAITING状态System.out.println("8: " + t5.getState());LockSupport.unpark(t5);}}
输出结果
1: NEW2: RUNNABLE0 1 23: TERMINATED4: WAITINGt2 go on!5: TIMED_WAITING6: BLOCKED7: WAITINGt3 得到了锁 o8: WAITINGt4 得到了锁 o
线程状态在Lock和synchronized的区别
- 等待synchronized锁时,线程处在BLOCKED状态,此时线程被放进了等待队列中,等待调度器调度执行
- 等待Lock锁时,线程处在WAITING状态===>JUC中的锁===>CAS来实现(忙等待)===>不会进入BLOCKED状态,而是进入WAITING状态
- 避免Lock.lock()和synchronized混在一起
- 只有synchronized会处于BLOCKED状态,而其他状态处于WAITING或者TIMED_WAITING状态
总结
- 6种状态分别在什么情况下出现总结
- BLOCKED算是一个比较严重的阻塞状态===>只有在参与争夺synchronized锁时会出现这种状态,其他的都没有
- synchronized是要经过操作系统去调度的。可以这么说,只有要经过操作系统调度的,才会有这个BLOCKED状态,其他情况下都没有===>去理发店理发,理发店比较忙,店长(操作系统)出来把你安排到旁边休息区(小屋)去等待休息,什么时候轮到你了会把你叫出来;而WAITING或者TIMED_WAITING是持有这把锁了、自己阻塞了、忙等待、不进小屋、就在理发的人旁边看着(等?),因为这个理发师特别好,所以就在旁边看着,等着去抢;店长说马上到你了,你先睡5秒,这就是TIMED_WAITING;理发师是cpu,他说能服务两个人,一个人在理发,一个人坐在理发椅上就行,理发师是一会服务你,一会服务他(正在服务的叫RUNNING,坐在椅子上的为READY)
- yield方法调用之后会让出cpu,自己进入等待队列,这时可能会把cpu让给等待队列中的另一线程,也有可能还是分配给原来的线程
- synchronized获取了哪把锁,就是用哪把锁的wait和notify
多线程不怎么能用到,写中间件的机会很少,写底层的机会也很少;所以重点应该放在面试问的多的地方——>面向面试学习
轻松简单!
