一、先来看看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 = 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 block
  15. 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. 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 block
  18. 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 = 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()输出的结果为:

  1. 小米等待王妈妈的呼唤
  2. 小阿等待王妈妈的呼唤
  3. 小百等待王妈妈的呼唤
  4. 进来一个吧
  5. 小米进入小姐的房间
  6. 进来一个吧
  7. 小阿进入小姐的房间
  8. 进来一个吧
  9. 小百进入小姐的房间

notifyAll()输出的结果为:

  1. 小米等待王妈妈的呼唤
  2. 小阿等待王妈妈的呼唤
  3. 小百等待王妈妈的呼唤
  4. 小百进入小姐的房间
  5. 小阿进入小姐的房间
  6. 小米进入小姐的房间

Reference

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

Java 多线程 线程的五种状态,线程 Sleep, Wait, notify, notifyAll的更多相关文章

  1. java线程的五种状态

    五种状态 开始状态(new) 就绪状态(runnable) 运行状态(running) 阻塞状态(blocked) 结束状态(dead) 状态变化 1.线程刚创建时,是new状态 2.线程调用了sta ...

  2. Java线程的五种状态详解

    状态转换图 1.new状态:通过new关键字创建了Thread或其子类的对象 2.Runnable状态:即就绪状态.可从三种状态到达,new状态的Thread对象调用start()方法,Running ...

  3. Java 多线程同步的五种方法

    一.引言 闲话不多说,进入正题. 二.为什么要线程同步 因为当我们有多个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会导致变量值或对象的状态出现混乱,从而导致程序异常.举个例子 ...

  4. 细说进程五种状态的生老病死——双胞胎兄弟Java线程

    java线程的五种状态其实要真正高清,只需要明白计算机操作系统中进程的知识,原理都是相同的. 系统根据PCB结构中的状态值控制进程. 单CPU系统中,任一时刻处于执行状态的进程只有一个. 进程的五种状 ...

  5. java核心知识点学习----并发和并行的区别,进程和线程的区别,如何创建线程和线程的四种状态,什么是线程计时器

    多线程并发就像是内功,框架都像是外功,内功不足,外功也难得精要. 1.进程和线程的区别 一个程序至少有一个进程,一个进程至少有一个线程. 用工厂来比喻就是,一个工厂可以生产不同种类的产品,操作系统就是 ...

  6. 并发编程——Java线程的6种状态及切换

    前言 本次主要分享一下Java线程的六种状态及其转换. 如果对于线程的创建方式不太了解,推荐观看并发编程--认识java里的线程 线程的状态及其转换 操作系统线程的五种状态 新建(NEW) 就绪(RU ...

  7. JAVA 线程的6种状态

    JAVA线程的6种状态 线程状态(Thread.State).线程处于下列状态的其中之一. 一个线程在一个时刻只能有一个状态.这些状态是虚拟机线程状态,不能反应任何操作系统的线程状态. 通过Threa ...

  8. 在java中怎样实现多线程?线程的4种状态

    一.在java中怎样实现多线程? extends Thread implement Runnable 方法一:继承 Thread 类,覆盖方法 run(),我们在创建的 Thread 类的子类中重写 ...

  9. java线程五种状态

    java线程五种状态: 创建 -> 就绪 -> 运行 -> 销毁 创建 -> 就绪 -> 运行 -> 等待(缺少资源) -> 销毁 下图:各种状态转换

随机推荐

  1. springmvc+jsp 拦截器下如何设置欢迎页面

    0.需求 如何让用户在浏览器地址栏键入[http://XXX.XXX.XX.XX:端口号/应用名/]以后自动跳转到系统的登录界面 1.web.xml 1.1 注意welcome-file-list的配 ...

  2. IE每次关闭都提示IE已停止工作

    方法一:打开IE浏览器,点击工具,选择下拉菜单中的internet选项,切换至高级选项卡标签,找到“启用第三方浏览器扩展”选项.把前面的打勾去掉: 方法二:打开IE浏览器,不管是6.0还是更高的版本这 ...

  3. iOS 应用中打开其他应用 (转)

    我们来讨论一下,在iOS开发中,如何实现从app1打开app2. 基本的思路就是,可以为app2定义一个URL,在app1中通过打开这个URL来打开app2,在此过程中,可以传送一些参数.下面来讨论一 ...

  4. lua 根据函数名字符串来执行函数

    function myfunction(msg) print("this is msg fun " .. msg); end local fun =_G["myfunct ...

  5. iOS iTuns Connect官方配置教程

    iTunes Connect 开发者指南 (iTunes Connect Developer Guide): https://developer.apple.com/library/ios/docum ...

  6. Spring容器AOP的理解

    一句话理解:根据被代理对象信息通过Proxy动态生成我们具体的代理类. 实现就动态代理.那动态代理是什么呢? 动态代理其实并不是什么新鲜的东西,学过设计模式的人都应该知道代理模式,代理模式就是一种静态 ...

  7. 【RF库测试】Encode String To Bytes&Decode Bytes To String& should be string&should be unicode string &should not be string

    场景1:判断类型 r ${d} set variable \xba\xcb\xbc\xf5\xcd\xa8\xb9\xfd #核减通过 Run Keyword And Continue On Fail ...

  8. 【RF库XML测试】Get Elements

    Name:Get ElementsSource:XML <test library>Arguments:[ source | xpath ]Returns a list of elemen ...

  9. popupWindow 在指定位置上的显示

    先看效果图,免得浪费大家时间,看是不是想要的效果 . 直接上代码 ,核心方法. private void showPopupWindow(View parent) { if (popupWindow  ...

  10. Android Studio 出现 Gradle's dependency cache may be corrupt 解决方案

    将 .\项目地址\gradle\wrapper\gradle-wrapper.properties 文件中的 gradle版本 与 正常的版本 修改一致即可.