- 不那么重要(主要帮助理解线程问题),面试很少遇到
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醒了之后会sleep
System.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: NEW
2: RUNNABLE
0 1 2
3: TERMINATED
4: WAITING
t2 go on!
5: TIMED_WAITING
6: BLOCKED
7: WAITING
t3 得到了锁 o
8: WAITING
t4 得到了锁 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
多线程不怎么能用到,写中间件的机会很少,写底层的机会也很少;所以重点应该放在面试问的多的地方——>面向面试学习
轻松简单!