当前位置: 代码迷 >> 综合 >> JAVA 多线程 线程的五种状态,线程 SLEEP, WAIT, NOTIFY, NOTIFYALL
  详细解决方案

JAVA 多线程 线程的五种状态,线程 SLEEP, WAIT, NOTIFY, NOTIFYALL

热度:70   发布时间:2023-10-25 04:11:09.0

一、先来看看Thread类里面都有哪几种状态,在Thread.class中可以找到这个枚举,它定义了线程的相关状态:

复制代码

1 public enum State {
2     NEW,
3     RUNNABLE,
4     BLOCKED,
5     WAITING,
6     TIMED_WAITING,
7     TERMINATED;
8 }

复制代码

具体解释请见源码,下面简单解释下Thread的五种状态什么时候出现:

  1. NEW 新建状态,线程创建且没有执行start方法时的状态
  2. RUNNABLE 可运行状态,线程已经启动,但是等待相应的资源(比如IO或者时间片切换)才能开始执行
  3. BLOCKED 阻塞状态,当遇到synchronized或者lock且没有取得相应的锁,就会进入这个状态
  4. WAITING 等待状态,当调用Object.wait或者Thread.join()且没有设置时间,在或者LockSupport.park时,都会进入等待状态。
  5. TIMED_WAITING 计时等待,当调用Thread.sleep()或者Object.wait(xx)或者Thread.join(xx)或者LockSupport.parkNanos或者LockSupport.partUntil时,进入该状态
  6. TERMINATED 终止状态,线程中断或者运行结束的状态

 

二、Sleep 与 Wait 的区别

由于wait方法是在Object上的,而sleep方法是在Thread上,当调用Thread.sleep时,并不能改变对象的状态,因此也不会释放锁。请看下面代码结果:

复制代码

 1 package springBootExample.example.simpleApplication;2 3 public class TestThread {4 5     public static void main(String[] args) {6         Room room = new Room();7         Thread man = new Thread(room, "男人");8         Thread female = new Thread(room, "女人");9         System.out.println("After new but before start thread name = "+man.getName()+" state = "+man.getState());
10         // 此时的man和female处于NEW状态
11         man.start();
12         System.out.println("After start Thread name ="+man.getName()+" state = "+man.getState());
13         female.start();
14         // 此时的man和female处于Runnable状态,但是等待相应的资源(比如IO或者时间片切换)才能开始执行,谁先获得资源就可以执行
15         System.out.println("小姐已经接待完客人");
16     }
17 
18 }
19 
20 class Room implements Runnable {
21     public int count = 1;
22 
23     @Override
24     public void run() {
25 
26         while (count <= 20) {
27             // BLOCKED 阻塞状态,当遇到synchronized或者lock且没有取得相应的锁,就会进入这个状态
28             synchronized (this) {
29                 System.out.println(Thread.currentThread().getName() + "去小姐的房间,小姐累计接待客人:" + count + "个.");
30                 count++;
31                 try {
32                     Thread.currentThread().sleep(100);
33                     // this.wait(100);
34                 } catch (InterruptedException e) {
35                     // TODO Auto-generated catch block
36                     e.printStackTrace();
37                 }
38             }
39         }
40 
41     }
42 
43 }

复制代码

结果:

复制代码

 1 After new but before start thread name = 男人 state = NEW2 After start Thread name =男人 state = RUNNABLE3 男人去小姐的房间,小姐累计接待客人:1个.4 小姐已经接待完客人5 男人去小姐的房间,小姐累计接待客人:2个.6 男人去小姐的房间,小姐累计接待客人:3个.7 男人去小姐的房间,小姐累计接待客人:4个.8 男人去小姐的房间,小姐累计接待客人:5个.9 男人去小姐的房间,小姐累计接待客人:6个.
10 男人去小姐的房间,小姐累计接待客人:7个.
11 男人去小姐的房间,小姐累计接待客人:8个.
12 男人去小姐的房间,小姐累计接待客人:9个.
13 男人去小姐的房间,小姐累计接待客人:10个.
14 男人去小姐的房间,小姐累计接待客人:11个.
15 男人去小姐的房间,小姐累计接待客人:12个.
16 男人去小姐的房间,小姐累计接待客人:13个.
17 男人去小姐的房间,小姐累计接待客人:14个.
18 男人去小姐的房间,小姐累计接待客人:15个.
19 男人去小姐的房间,小姐累计接待客人:16个.
20 男人去小姐的房间,小姐累计接待客人:17个.
21 男人去小姐的房间,小姐累计接待客人:18个.
22 男人去小姐的房间,小姐累计接待客人:19个.
23 男人去小姐的房间,小姐累计接待客人:20个.
24 女人去小姐的房间,小姐累计接待客人:21个.

复制代码

从上面的结果可以看出,NEW状态在新创建一个线程时呈现,RUNNABLE是在线程调用start()方法。因为线程获得资源就可以执行,在main()方法中新建一个线程man.start()执行,因此新线程获得资源就可以执行,从第4行结果看出。

注意看最后面有一个女人。这是因为synchronized的代码同步时在while循环里面,因此最后一次男人和女人都进入到了while里面,然后才开始等待相应的锁。这就导致第20次执行完轮到了女人。

当调用wait时:

复制代码

 1 @Override2     public void run() {3 4         while (count <= 20) {5             // BLOCKED 阻塞状态,当遇到synchronized或者lock且没有取得相应的锁,就会进入这个状态6 //            System.out.println("Before synchronized thread name = "+Thread.currentThread().getName()+" state = "+Thread.currentThread().getState());7             synchronized (this) {8                 System.out.println(Thread.currentThread().getName() + "去小姐的房间,小姐累计接待客人:" + count + "个.");9                 count++;
10                 try {
11 //                    Thread.currentThread().sleep(100);
12                      this.wait(100);
13                 } catch (InterruptedException e) {
14                     // TODO Auto-generated catch block
15                     e.printStackTrace();
16                 }
17             }
18         }

复制代码

 

结果:

复制代码

 1 After new but before start thread name = 男人 state = NEW2 After start Thread name =男人 state = RUNNABLE3 小姐已经接待完客人4 男人去小姐的房间,小姐累计接待客人:1个.5 女人去小姐的房间,小姐累计接待客人:2个.6 男人去小姐的房间,小姐累计接待客人:3个.7 女人去小姐的房间,小姐累计接待客人:4个.8 女人去小姐的房间,小姐累计接待客人:5个.9 男人去小姐的房间,小姐累计接待客人:6个.
10 女人去小姐的房间,小姐累计接待客人:7个.
11 男人去小姐的房间,小姐累计接待客人:8个.
12 男人去小姐的房间,小姐累计接待客人:9个.
13 女人去小姐的房间,小姐累计接待客人:10个.
14 男人去小姐的房间,小姐累计接待客人:11个.
15 女人去小姐的房间,小姐累计接待客人:12个.
16 男人去小姐的房间,小姐累计接待客人:13个.
17 女人去小姐的房间,小姐累计接待客人:14个.
18 男人去小姐的房间,小姐累计接待客人:15个.
19 女人去小姐的房间,小姐累计接待客人:16个.
20 男人去小姐的房间,小姐累计接待客人:17个.
21 女人去小姐的房间,小姐累计接待客人:18个.
22 男人去小姐的房间,小姐累计接待客人:19个.
23 女人去小姐的房间,小姐累计接待客人:20个.

复制代码

 

 

但是如果稍作修改就会出现弄一种情况,代码如下:

复制代码

 1 class Room implements Runnable {2     public int count = 1;3 4     @Override5     public void run() {6 7         while (count <= 20) {8             // BLOCKED 阻塞状态,当遇到synchronized或者lock且没有取得相应的锁,就会进入这个状态9             System.out.println("Before synchronized thread name = "+Thread.currentThread().getName()+" state = "+Thread.currentThread().getState());
10             synchronized (this) {
11                 System.out.println(Thread.currentThread().getName() + "去小姐的房间,小姐累计接待客人:" + count + "个.");
12                 count++;
13                 try {
14                     Thread.currentThread().sleep(100);
15                     // this.wait(100);
16                 } catch (InterruptedException e) {
17                     // TODO Auto-generated catch block
18                     e.printStackTrace();
19                 }
20             }
21         }
22 
23     }

复制代码

结果:

复制代码

 1 After new but before start thread name = 男人 state = NEW2 After start Thread name =男人 state = RUNNABLE3 Before synchronized thread name = 男人 state = RUNNABLE4 小姐已经接待完客人5 男人去小姐的房间,小姐累计接待客人:1个.6 Before synchronized thread name = 女人 state = RUNNABLE7 Before synchronized thread name = 男人 state = RUNNABLE8 女人去小姐的房间,小姐累计接待客人:2个.9 Before synchronized thread name = 女人 state = RUNNABLE
10 男人去小姐的房间,小姐累计接待客人:3个.
11 Before synchronized thread name = 男人 state = RUNNABLE
12 女人去小姐的房间,小姐累计接待客人:4个.
13 Before synchronized thread name = 女人 state = RUNNABLE
14 男人去小姐的房间,小姐累计接待客人:5个.
15 Before synchronized thread name = 男人 state = RUNNABLE
16 女人去小姐的房间,小姐累计接待客人:6个.
17 Before synchronized thread name = 女人 state = RUNNABLE
18 男人去小姐的房间,小姐累计接待客人:7个.
19 Before synchronized thread name = 男人 state = RUNNABLE
20 女人去小姐的房间,小姐累计接待客人:8个.
21 Before synchronized thread name = 女人 state = RUNNABLE
22 男人去小姐的房间,小姐累计接待客人:9个.
23 Before synchronized thread name = 男人 state = RUNNABLE
24 女人去小姐的房间,小姐累计接待客人:10个.
25 Before synchronized thread name = 女人 state = RUNNABLE
26 男人去小姐的房间,小姐累计接待客人:11个.
27 Before synchronized thread name = 男人 state = RUNNABLE
28 女人去小姐的房间,小姐累计接待客人:12个.
29 Before synchronized thread name = 女人 state = RUNNABLE
30 男人去小姐的房间,小姐累计接待客人:13个.
31 Before synchronized thread name = 男人 state = RUNNABLE
32 女人去小姐的房间,小姐累计接待客人:14个.
33 Before synchronized thread name = 女人 state = RUNNABLE
34 男人去小姐的房间,小姐累计接待客人:15个.
35 Before synchronized thread name = 男人 state = RUNNABLE
36 女人去小姐的房间,小姐累计接待客人:16个.
37 Before synchronized thread name = 女人 state = RUNNABLE
38 男人去小姐的房间,小姐累计接待客人:17个.
39 Before synchronized thread name = 男人 state = RUNNABLE
40 女人去小姐的房间,小姐累计接待客人:18个.
41 Before synchronized thread name = 女人 state = RUNNABLE
42 男人去小姐的房间,小姐累计接待客人:19个.
43 Before synchronized thread name = 男人 state = RUNNABLE
44 女人去小姐的房间,小姐累计接待客人:20个.
45 男人去小姐的房间,小姐累计接待客人:21个.

复制代码

目前这种现象暂时还不是特别清楚原理,但是当男人和女人都在while循环等待时,Thread.currentThread().getName() 会获取当前线程的名字,而在循环中再获取当前名字时会出现这种交替的情况?其实Room资源一直是男人拥有。

三、Wait(), Notify() , NotifyAll()的使用

wait、notify、notifyall这几个一般都一起使用。不过需要注意下面几个重要的点:

  1. 调用wait\notify\notifyall方法时,需要与锁或者synchronized搭配使用,不然会报错java.lang.IllegalMonitorStateException,因为任何时刻,对象的控制权只能一个线程持有,因此调用wait等方法的时候,必须确保对其的控制权。
  2. 如果对简单的对象调用wait等方法,如果对他们进行赋值也会报错,因为赋值相当于修改的原有的对象,因此如果有修改需求可以外面包装一层。
  3. notify可以唤醒一个在该对象上等待的线程,notifyAll可以唤醒所有等待的线程。
  4. wait(xxx) 可以挂起线程,并释放对象的资源,等计时结束后自动恢复;wait()则必须要其他线程调用notify或者notifyAll才能唤醒。

复制代码

 1 package springBootExample.example.simpleApplication;2 3 public class TestWaitAndNotify {4     Call call = new Call(false);5 6     class MaMa extends Thread {7         public MaMa(String name) {8             super(name);9         }
10 
11         @Override
12         public void run() {
13             synchronized (call) {
14                 try {
15                     call.wait(3000);
16                 } catch (InterruptedException e) {
17                     // TODO Auto-generated catch block
18                     e.printStackTrace();
19                 }
20                 call.setFlag(true);
21                 // call.notifyAll();
22                 for (int i = 0; i < 3; i++) {
23                     System.out.println("进来一个吧");
24                     call.notify();
25                     try {
26                         call.wait(1000);
27                     } catch (InterruptedException e) {
28                         e.printStackTrace();
29                     }
30                 }
31             }
32 
33         }
34 
35     }
36 
37     class Customer extends Thread {
38         public Customer(String name) {
39             super(name);
40         }
41 
42         @Override
43         public void run() {
44             synchronized (call) {
45                 while (!call.isFlag()) {
46                     System.out.println(Thread.currentThread().getName() + "等待王妈妈的呼唤");
47                     try {
48                         call.wait();
49                     } catch (InterruptedException e) {
50                         // TODO Auto-generated catch block
51                         e.printStackTrace();
52                     }
53                 }
54                 System.out.println(Thread.currentThread().getName() + "进入小姐的房间");
55             }
56         }
57     }
58 
59     public static void main(String[] args) {
60         TestWaitAndNotify test = new TestWaitAndNotify();
61         MaMa teacher = test.new MaMa("王妈妈");
62         Customer stu1 = test.new Customer("小米");
63         Customer stu2 = test.new Customer("小百");
64         Customer stu3 = test.new Customer("小阿");
65         teacher.start();
66         stu1.start();
67         stu2.start();
68         stu3.start();
69 
70     }
71 
72 }
73 
74 class Call {
75     private boolean flag = false;
76 
77     public Call(boolean flag) {
78         this.flag = flag;
79     }
80 
81     public boolean isFlag() {
82         return flag;
83     }
84 
85     public void setFlag(boolean flag) {
86         this.flag = flag;
87     }
88 
89 }

复制代码

上面代码中21,24行包含了notify() 和notifyAll()方法的,61行注意内部类实例时的方法。代码的运行结果也会不相同,notify()输出的结果为:

复制代码

小米等待王妈妈的呼唤
小阿等待王妈妈的呼唤
小百等待王妈妈的呼唤
进来一个吧
小米进入小姐的房间
进来一个吧
小阿进入小姐的房间
进来一个吧
小百进入小姐的房间

复制代码

notifyAll()输出的结果为:

小米等待王妈妈的呼唤
小阿等待王妈妈的呼唤
小百等待王妈妈的呼唤
小百进入小姐的房间
小阿进入小姐的房间
小米进入小姐的房间

 

Reference

[1] http://www.cnblogs.com/xing901022/p/7846809.html

 

  相关解决方案