当线程被创建并启动以后,并没有直接进入执行状态,还有其他的状态。
在线程的生命周期中, java.lang.Thread.State 这个枚举中给出了六种线程状态:
线程之间的状态转换:
想要实现多线程,必须在主线程中创建新的线程对象。Java 语言使用 Thread 类及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五种状
新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
就绪: 处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
运行: 当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线程的操作和功
阻塞: 在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态
死亡: 线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
线程的生命周期:
从运行状态到阻塞状态有几种情况:
① sleep() 休眠;
② wait() 等待;
③ join() 加塞;
④ suspend() 挂起,已过时;
从阻塞回到就绪状态:
① sleep() 时间到,sleep() 被打断 interrupt();
② notify() 唤醒;
③ 加塞的线程结束
④ 占用锁的线程释放锁
⑤ resume() 解挂,已过时;
从运行到死亡:① run()正常结束 ② run()遇到异常但是没处理 ③ 其他线程把你stop()(已过时)
示意图:
Timed Waiting在API中的描述为:一个正在限时等待另一个线程执行一个(唤醒)动作的线程处于这一状态。
当调用了 sleep 方法之后,当前执行的线程就进入 “休眠状态”,其实就是Timed Waiting(计时等待)。
Demo:
1 public class MyThread extends Thread {
2 public void run() {
3 for (int i = 0; i < 100; i++) {
4 if (i % 10 == 0) {
5 System.out.println("‐‐‐‐‐‐‐" + i);
6 }
7 System.out.print(i);
8 try {
9 Thread.sleep(1000);
10 System.out.print(" 线程睡眠1秒!\n");
11 } catch (InterruptedException e) {
12 e.printStackTrace();
13 }
14 }
15 }
16 public static void main(String[] args) {
17 new MyThread().start();
18 }
19 }
注意:
1. 进入 TIMED_WAITING 状态的一种常见情形是调用的 sleep 方法,单独的线程也可以调用,不一定非要有协作关系
2. 为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程中会睡眠
3. sleep与锁无关,线程睡眠到期自动苏醒,并返回到Runnable(可运行)状态。
Tips: sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始立刻执行。
线程状态图:
Blocked状态在API中的介绍为:一个正在阻塞等待一个监视器锁(锁对象)的线程处于这一状态。
如当线程 A 与线程B 在代码中使用同一个锁,如果线程 A 获取到锁,线程A 进入 Runnable 状态,那么线程B就进入到 Blocked 锁阻塞状态。
Blocked 线程状态图:
Waiting 状态介绍为:一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态。
Demo:
1 public class WaitingTest {
2 public static Object obj = new Object();
3 public static void main(String[] args) {
4 // 演示waiting
5 new Thread(new Runnable() {
6 @Override
7 public void run() {
8 while (true){
9 synchronized (obj){
10 try {
11 System.out.println( Thread.currentThread().getName() +"=== 获取到锁对象,调用wait方法,进入waiting状态,释放锁对象");
12 obj.wait(); //无限等待
13 //obj.wait(5000); //计时等待, 5秒 时间到,自动醒来
14 } catch (InterruptedException e) {
15 e.printStackTrace();
16 }
17 System.out.println( Thread.currentThread().getName() + "=== 从waiting状
18 态醒来,获取到锁对象,继续执行了");
19 }
20 }
21 }
22 },"等待线程").start();
23
24 new Thread(new Runnable() {
25 @Override
26 public void run() {
27 // while (true){ //每隔3秒 唤醒一次
28 try {
29 System.out.println( Thread.currentThread().getName() +"‐‐‐‐‐ 等待3秒钟");
30 Thread.sleep(3000);
31 } catch (InterruptedException e) {
32 e.printStackTrace();
33 }
34 synchronized (obj){
35 System.out.println( Thread.currentThread().getName() +"‐‐‐‐‐ 获取到锁对
36 象,调用notify方法,释放锁对象");
37 obj.notify();
38 }
39 }
40 // }
41 },"唤醒线程").start();
42 }
43 }
一个调用了某个对象的 Object.wait 方法的线程会等待另一个线程调用此对象的 Object.notify() 方法或 Object.notifyAll() 方法。
注意:waiting 状态并不是一个线程的操作,它体现的是多个线程间的通信,可以理解为多个线程之间的协作关系,多个线程会争取锁,同时相互之间又存在协作关系。
扩展:
当多个线程协作时,比如A,B线程,如果A线程在Runnable(可运行)状态中调用了wait()方法那么A线程就进入了Waiting(无限等待)状态,同时失去了同步锁。
假如这个时候B线程获取到了同步锁,在运行状态中调用了notify()方法,那么就会将无限等待的A线程唤醒。
注意是唤醒,如果获取到锁对象,那么A线程唤醒后就进入Runnable(可运行)状态;如果没有获取锁对象,那么就进入到Blocked(锁阻塞状态)。
Waiting 线程状态图:
Tips:
发现Timed Waiting(计时等待) 与 Waiting(无限等待) 状态联系还是很紧密的,比如Waiting(无限等待) 状态中wait方法是空参的,而timed waiting(计时等待) 中wait方法是带参的。
这种带参的方法,其实是一种倒计时操作,相当于我们生活中的小闹钟,我们设定好时间,到时通知,可是如果提前得到(唤醒)通知,那么设定好时间在通知也就显得多此一举了,那么这种设计方案其实是一举两得。
如果没有得到(唤醒)通知,那么线程就处于Timed Waiting状态,直到倒计时完毕自动醒来;如果在倒计时期间得到(唤醒)通知,那么线程从Timed Waiting状态立刻唤醒。
原文:https://www.cnblogs.com/niujifei/p/14403799.html