线程状态
1 线程状态概述
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是⼀直处于执⾏状态。在线
程的⽣命周期中,有⼏种状态呢?在 API 中 java.lang.Thread.State 这个枚举中给出了六种线程状态:
线程状态 | 导致状态发生的条件 |
---|---|
new (新建) | 线程刚被创建,但是并未启动。还没调用start方法 |
Runable(可运行) | 线程可以在java 虚拟机中运行的状态,可能正在运行自己的代码,也可能没有。这个取决于操作系统处理器 |
Blocked(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象被其他的线程持有,则给线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态 |
Waiting(⽆限等待) | ⼀个线程在等待另⼀个线程执⾏⼀个(唤醒)动作时,该线程进⼊Waiting状态。进⼊这个状态后是不能⾃动唤醒的,必须等待另⼀个线程调⽤notify或者notifyAll⽅法才能够唤醒。 |
Timed Waiting(计时等待) | 同waiting状态,有⼏个⽅法有超时参数,调⽤他们将进⼊TimedWaiting状态。这⼀状态将⼀保持到超时期满或者接收到唤醒通知。带有超时参数的常⽤⽅法有Thread.sleep、Object.wait |
Teminated(被终⽌) | 因为run⽅法正常退出⽽死亡,或者因为没有捕获的异常终⽌了run⽅法⽽死亡。 |
2 Timed Waiting(计时等待)
在卖票的案例中,为了减少线程执行太快,现象不明显等问题,我们在run方法中添加了sleep语句,这样就强制当前正字执行的线程休眠(暂停执行),以“减缓线程”。
调用sleep方法之后,当前执行的线程就会进入到“休眠状态”,其实就是所谓的Timed Waiting(计时等待),那么通过一个案例加深对该状态的理解。
实现一个计数器,计数到100,在每个数字机之间暂停1秒,每隔10个数字输出一个字符串。
public class MyThread extends Thread {
public void run() {
for (int i = 0; i < 100; i++) {
if ((i) % 10 == 0) {
System.out.println("-------" + i);
}
System.out.print(i);
try {
Thread.sleep(1000);
System.out.print(" 线程睡眠1秒!\n");
} catch (InterruptedException e) {
e.printStackTrace(); }
}
}
public static void main(String[] args) {
new MyThread().start();
}
}
- 进⼊ TIMED_WAITING 状态的⼀种常⻅情形是调⽤的 sleep ⽅法,单独的线程也可以调⽤,
不⼀定⾮要有协作关系。 - 为了让其他线程有机会执⾏,可以将Thread.sleep()的调⽤放线程run()之内。这样才能保证
该线程执⾏过程中会睡眠。 - sleep与锁⽆关,线程睡眠到期⾃动苏醒,并返回到Runnable(可运⾏)状态
sleep()中指定的时间是线程不会运⾏的最短时间。因此,sleep()⽅法不能保证该线程睡眠到期后就开始⽴刻执⾏。
Timed Waiting 线程状态图:
3 BLOCKED(锁阻塞)
Blocked 状态在 API 中的介绍为:⼀个正在阻塞等待⼀个监视器锁(锁对象)的线程处于这⼀状态
⽐如,线程A与线程B代码中使⽤同⼀锁,如果线程A获取到锁,线程A进⼊到 Runnable 状态,那么线程B就进⼊到 Blocked 锁阻塞状态。
这是由 Runnable 状态进⼊ Blocked 状态。除此 Waiting 以及 Time Waiting 状态也会在某种情
况下进⼊阻塞状态。
Blocked 线程状态图:
4 Waiting(⽆限等待)
Wating 状态在 API 中介绍为:⼀个正在⽆限期等待(waiting)另⼀个线程执⾏⼀个特别的notify()/notifyAll()(唤醒)动作的线程处于这⼀状态。
public class WaitingTest {
public static Object obj = new Object();
public static void main(String[] args) {
// 演示waiting
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (obj) {
try {
System.out.println(Thread.currentThread().getName() + "=== 获
取到锁对象,调⽤wait⽅法,进⼊waiting状态,释放锁对象");
obj.wait(); // ⽆限等待
// obj.wait(5000); // 计时等待, 5秒 时间到,⾃动醒来
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "=== 从waiting状态醒来,获取到锁对象,继续执⾏了");
}
}
}
}, "等待线程").start();
new Thread(new Runnable() {
@Override
public void run() {
// while (true) { // 每隔3秒 唤醒⼀次
try {
System.out.println(Thread.currentThread().getName() + "----- 等 待3秒钟");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj) {
System.out.println(Thread.currentThread().getName() + "----- 获
取到锁对象,调⽤notify⽅法,释放锁对象");
obj.notify();
}
}
// }
}, "唤醒线程").start();
}
}
1.通过上述案例发现:一个调用了某个对象的Object.wait()方法的线程会等待另一个线程调用此对象的Object.notify方法或者Object.notifyAll()方法。
2.其实 waiting 状态并不是⼀个线程的操作,它体现的是多个线程间的通信,可以理解为多个线程
之间的协作关系,多个线程会争取锁,同时相互之间⼜存在协作关系。
3.当多个线程协作时,⽐如A,B线程,如果A线程在 Runnable(可运⾏)状态中调⽤了 wait() ⽅
法那么A线程就进⼊了Waiting(⽆限等待)状态,同时失去了同步锁。假如这个时候B线程获取
到了同步锁,在运⾏状态中调⽤了 notify() ⽅法,那么就会将⽆限等待的A线程唤醒。注意是唤
醒,如果获取到锁对象,那么A线程唤醒后就进⼊ Runnable(可运⾏)状态;如果没有获取锁对
象,那么就进⼊到 Blocked(锁阻塞状态)
Waiting 线程状态图:
5 sleep()方法和wait()方法的区别和共同点
1.两者最主要的区别在于:sleep() 方法没有释放锁,而 wait() 方法释放了锁 。
2.两者都可以暂停线程的执行。
3.wait() 通常被用于线程间交互/通信,sleep() 通常被用于暂停执行。
4.wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。
sleep() 方法执行完成后,线程会自动苏醒。或者可以使用 wait(long timeout) 超时后线程会自动苏醒。