博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 多线程 线程的五种状态,线程 Sleep, Wait, notify, notifyAll
阅读量:6038 次
发布时间:2019-06-20

本文共 10460 字,大约阅读时间需要 34 分钟。

一、先来看看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     @Override24     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 block36                     e.printStackTrace();37                 }38             }39         }40 41     }42 43 }

结果:

1 After new but before start thread name = 男人 state = NEW 2 After start Thread name =男人 state = RUNNABLE 3 男人去小姐的房间,小姐累计接待客人: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 @Override 2     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 block15                     e.printStackTrace();16                 }17             }18         }

 

结果:

1 After new but before start thread name = 男人 state = NEW 2 After start Thread name =男人 state = RUNNABLE 3 小姐已经接待完客人 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     @Override 5     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 block18                     e.printStackTrace();19                 }20             }21         }22 23     }

结果:

1 After new but before start thread name = 男人 state = NEW 2 After start Thread name =男人 state = RUNNABLE 3 Before synchronized thread name = 男人 state = RUNNABLE 4 小姐已经接待完客人 5 男人去小姐的房间,小姐累计接待客人:1个. 6 Before synchronized thread name = 女人 state = RUNNABLE 7 Before synchronized thread name = 男人 state = RUNNABLE 8 女人去小姐的房间,小姐累计接待客人:2个. 9 Before synchronized thread name = 女人 state = RUNNABLE10 男人去小姐的房间,小姐累计接待客人:3个.11 Before synchronized thread name = 男人 state = RUNNABLE12 女人去小姐的房间,小姐累计接待客人:4个.13 Before synchronized thread name = 女人 state = RUNNABLE14 男人去小姐的房间,小姐累计接待客人:5个.15 Before synchronized thread name = 男人 state = RUNNABLE16 女人去小姐的房间,小姐累计接待客人:6个.17 Before synchronized thread name = 女人 state = RUNNABLE18 男人去小姐的房间,小姐累计接待客人:7个.19 Before synchronized thread name = 男人 state = RUNNABLE20 女人去小姐的房间,小姐累计接待客人:8个.21 Before synchronized thread name = 女人 state = RUNNABLE22 男人去小姐的房间,小姐累计接待客人:9个.23 Before synchronized thread name = 男人 state = RUNNABLE24 女人去小姐的房间,小姐累计接待客人:10个.25 Before synchronized thread name = 女人 state = RUNNABLE26 男人去小姐的房间,小姐累计接待客人:11个.27 Before synchronized thread name = 男人 state = RUNNABLE28 女人去小姐的房间,小姐累计接待客人:12个.29 Before synchronized thread name = 女人 state = RUNNABLE30 男人去小姐的房间,小姐累计接待客人:13个.31 Before synchronized thread name = 男人 state = RUNNABLE32 女人去小姐的房间,小姐累计接待客人:14个.33 Before synchronized thread name = 女人 state = RUNNABLE34 男人去小姐的房间,小姐累计接待客人:15个.35 Before synchronized thread name = 男人 state = RUNNABLE36 女人去小姐的房间,小姐累计接待客人:16个.37 Before synchronized thread name = 女人 state = RUNNABLE38 男人去小姐的房间,小姐累计接待客人:17个.39 Before synchronized thread name = 男人 state = RUNNABLE40 女人去小姐的房间,小姐累计接待客人:18个.41 Before synchronized thread name = 女人 state = RUNNABLE42 男人去小姐的房间,小姐累计接待客人:19个.43 Before synchronized thread name = 男人 state = RUNNABLE44 女人去小姐的房间,小姐累计接待客人: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         @Override12         public void run() {13             synchronized (call) {14                 try {15                     call.wait(3000);16                 } catch (InterruptedException e) {17                     // TODO Auto-generated catch block18                     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         @Override43         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 block51                         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

 

转载于:https://www.cnblogs.com/hoojjack/p/7932107.html

你可能感兴趣的文章
HTML_HTML标签区分是IE浏览器还是其它浏览器_已迁移
查看>>
我的友情链接
查看>>
nslookup工具的使用方法
查看>>
eclipse插件
查看>>
1、Go HTTP框架Beego - Beego简介
查看>>
Android笔记:通过RadioGroup/RadioButton自定义tabhost的简单方法
查看>>
java 几种远程服务调用协议的比较
查看>>
IOS CGContextSetLineWidth无法设置1像素线宽?
查看>>
Struts中的一个简单上传图片操作
查看>>
Android 保存浏览记录 SharedPreTools
查看>>
C++实现动态加载资源(一)
查看>>
Android 动画叠加效果
查看>>
Aop RealProxy 千年遇BUG
查看>>
根据屏幕分辨率修改css样式
查看>>
产品无间道——如何毁掉对手产品
查看>>
VS2010编译过程中遇到的一个问题
查看>>
SO_REUSEADDR例解
查看>>
VC连接MySQL
查看>>
android sensor 之手掌接近或远离
查看>>
关于DB2对其sql语句进行长度限制的设置语句
查看>>