通过两个小栗子来说说Java的sleep、wait、notify、notifyAll的用法
2017-11-16 22:11
489 查看
线程是计算程序运行的最小载体,由于单个单核CPU的硬件水平发展到了一定的瓶颈期,因此就出现了多核多CPU的情况,直接就导致程序员多线程编程的复杂。由此可见线程对于高性能开发的重要性。
那么线程在计算机中有好几种状态,他们之间是怎么切换的?sleep和wait又有什么区别?notify和notifyAll怎么用?带着这些问题,我们来看看Java的线程吧!
Thread的状态
先来看看Thread类里面都有哪几种状态,在Thread.class中可以找到这个枚举,它定义了线程的相关状态:public enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; }
如下图所示:
NEW 新建状态,线程创建且没有执行start方法时的状态
RUNNABLE 可运行状态,线程已经启动,但是等待相应的资源(比如IO或者时间片切换)才能开始执行
BLOCKED 阻塞状态,当遇到synchronized或者lock且没有取得相应的锁,就会进入这个状态
WAITING 等待状态,当调用
Object.wait或者
Thread.join()且没有设置时间,在或者
LockSupport.park时,都会进入等待状态。
TIMED_WAITING 计时等待,当调用
Thread.sleep()或者
Object.wait(xx)或者
Thread.join(xx)或者
LockSupport.parkNanos或者
LockSupport.partUntil时,进入该状态
TERMINATED 终止状态,线程中断或者运行结束的状态
先来sleep和wait的区别
由于wait方法是在Object上的,而sleep方法是在Thread上,当调用Thread.sleep时,并不能改变对象的状态,因此也不会释放锁。这让我想起来我家的两个主子,一只泰迪一只美短,虽然他们两个是不同的物种,但是却有着相同的爱好,就是爱吃牛肉。偶尔给它们两个开荤,奈何只有一个食盆,每次只能一个主子吃肉。这就好比是两个线程,在争用同一个变量。如果使用thread.sleep,那么其中一个吃完一块肉后,会霸占食盆,不给另一只吃(不会释放锁等资源);如果使用wait,那么吃肉时,会离开食盆,这样就有机会让另一只去吃了,即占用的资源会释放。
详细的看一下下面的代码:
package cn.xingoo.test.basic.thread; public class AnimalEat { public static void main(String[] args) { System.out.println("盆里有20块肉"); Animal animal = new Animal(); try{ Thread tidy = new Thread(animal,"泰迪"); Thread cat = new Thread(animal,"美短"); tidy.start(); cat.start(); }catch (Exception e){ e.printStackTrace(); } System.out.println("盆里的肉吃完了!"); } } class Animal implements Runnable { int count = 0; @Override public void run() { while(count < 20){ synchronized (this){ try { System.out.println(Thread.currentThread().getName()+"吃力第"+count+"块肉"); count++; //Thread.sleep(100); this.wait(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
当使用this.wait(100)的时候,会输出下面的信息:
盆里有20块肉 泰迪吃力第0块肉 美短吃力第1块肉 盆里的肉吃完了! 泰迪吃力第2块肉 美短吃力第3块肉 泰迪吃力第4块肉 美短吃力第5块肉 泰迪吃力第6块肉 美短吃力第7块肉 泰迪吃力第8块肉 美短吃力第9块肉 泰迪吃力第10块肉 美短吃力第11块肉 美短吃力第12块肉 泰迪吃力第13块肉 美短吃力第14块肉 泰迪吃力第15块肉 美短吃力第16块肉 泰迪吃力第17块肉 美短吃力第18块肉 泰迪吃力第19块肉
可以发现,输出的信息并不是完美的交替,这是因为调用wait之后,并不一定马上时另一个线程执行,而是要根据CPU的时间分片轮转等其他的条件来定,轮到谁就看运气了。
当使用Thread.sleep(100)的时候,可以得到下面的信息:
盆里有20块肉 泰迪吃力第0块肉 盆里的肉吃完了! 泰迪吃力第1块肉 泰迪吃力第2块肉 泰迪吃力第3块肉 泰迪吃力第4块肉 泰迪吃力第5块肉 泰迪吃力第6块肉 泰迪吃力第7块肉 泰迪吃力第8块肉 泰迪吃力第9块肉 泰迪吃力第10块肉 泰迪吃力第11块肉 泰迪吃力第12块肉 泰迪吃力第13块肉 泰迪吃力第14块肉 泰迪吃力第15块肉 泰迪吃力第16块肉 泰迪吃力第17块肉 泰迪吃力第18块肉 美短吃力第19块肉 泰迪吃力第20块肉
注意看最后面有一只美短。这是因为synchronized的代码同步时在while循环里面,因此最后一次两个主子都进入到了while里面,然后才开始等待相应的锁。这就导致第19次轮到了另一个主子。
总结来说,sleep不会释放线程的锁,wait会释放线程的资源。
再谈谈wait与notify和notifyall
wait、notify、notifyall这几个一般都一起使用。不过需要注意下面几个重要的点:调用wait\notify\notifyall方法时,需要与锁或者synchronized搭配使用,不然会报错
java.lang.IllegalMonitorStateException,因为任何时刻,对象的控制权只能一个线程持有,因此调用wait等方法的时候,必须确保对其的控制权。
如果对简单的对象调用wait等方法,如果对他们进行赋值也会报错,因为赋值相当于修改的原有的对象,因此如果有修改需求可以外面包装一层。
notify可以唤醒一个在该对象上等待的线程,notifyAll可以唤醒所有等待的线程。
wait(xxx) 可以挂起线程,并释放对象的资源,等计时结束后自动恢复;wait()则必须要其他线程调用notify或者notifyAll才能唤醒。
举个通俗点的例子,我记得在高中的时候,每天上午快放学的时候大家都很紧张——因为那个时候小饭馆正好播放一些港台剧,大家就总愿意抢电视机旁边的位置,所以每次快要中午放学的时候,大家都做好冲刺跑步的准备。
但是有的老师总愿意压堂,搞的大家怨声载道。
比如,下面这位老师有的时候会用notifyall通知大家集体放学;有的时候会检查背书,背好了,才能走。
package cn.xingoo.test.basic.thread; public class School { private DingLing dingLing = new DingLing(false); class Teacher extends Thread{ Teacher(String name){ super(name); } @Override public void run() { synchronized (dingLing){ try { dingLing.wait(3000); } catch (InterruptedException e) { e.printStackTrace(); } dingLing.flag = true; System.out.println("放学啦"); dingLing.notifyAll(); /*for (int i = 0; i < 3; i++) { System.out.println("放一个走吧"); dingLing.notify(); try { dingLing.wait(1000); } catch (InterruptedException e) { e.printStackTrace(); } }*/ } } } class Student extends Thread{ Student(String name){ super(name); } @Override public void run(){ synchronized (dingLing){ while(!dingLing.flag){ System.out.println(Thread.currentThread().getName()+"开始等待"); try { dingLing.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"去吃饭啦"); } } } public static void main(String[] args) { School school = new School(); Teacher teacher = school.new Teacher("老师"); Student zhangsan = school.new Student("张三"); Student lisi = school.new Student("李四"); Student wangwu = school.new Student("王五"); teacher.start(); zhangsan.start(); lisi.start(); wangwu.start(); } } class DingLing{ Boolean flag = false; public DingLing(Boolean flag){ this.flag = flag; } }
当老师统一喊放学的时候,即调用
dingLing.notifyAll();,会得到下面的输出:
张三开始等待 李四开始等待 王五开始等待 放学啦 王五去吃饭啦 李四去吃饭啦 张三去吃饭啦
如果检查背书,那么每次老师只会调用一次notify,让一个同学(线程)走(工作),就会得到下面的输出:
张三开始等待 李四开始等待 王五开始等待 放一个走吧 张三去吃饭啦 放一个走吧 李四去吃饭啦 放一个走吧 王五去吃饭啦
注意的是,调用wait可以释放dingling的占用,这样才能让别的线程进行检查,如果改成Thread.sleep,有兴趣的童鞋就可以自己去看看效果啦!
参考
最简单的实例说明wait、notify、notifyAll的使用方法:http://longdick.iteye.com/blog/453615Java sleep和wait的区别:http://www.jb51.net/article/113587.htm
sleep和wait解惑:https://www.zhihu.com/question/23328075
相关文章推荐
- Java线程中sleep()、wait()和notify()和notifyAll()、yield()、join()等方法的用法和区别
- Java线程中sleep()、wait()和notify()和notifyAll()、yield()、join()等方法的用法和区别
- Java线程中sleep()、wait()和notify()和notifyAll()、suspend和resume()、yield()、join()、interrupt()的用法和区别
- Java线程中sleep()、wait()和notify()和notifyAll()、yield()、join()等方法的用法和区别
- Java线程中sleep()、wait()和notify()和notifyAll()、suspend和resume()、yield()、join()、interrupt()的用法和区别
- Java线程中sleep()、wait()和notify()和notifyAll()、yield()、join()等方法的用法和区别
- Java线程中sleep()、wait()和notify()和notifyAll()、suspend和resume()、yield()、join()、interrupt()的用法和区别
- Java线程中sleep()、wait()和notify()和notifyAll()、yield()、join()等方法的用法和区别
- Java线程中sleep()、wait()和notify()和notifyAll()、suspend和resume()、yield()、join()、interrupt()的用法和区别
- Java线程中sleep()、wait()和notify()和notifyAll()、yield()、join()等方法的用法和区别
- 【Java Concurrency】sleep()、wait()、notify()、notifyAll()的用法与区别
- Java线程中sleep()、wait()和notify()和notifyAll()、yield()、join()等方法的用法和区别
- Java 线程协作 wait,notify,notifyAll,Sleep,Join,Condition
- 在Java线程中,wait、notify、notifyAll的用法
- java中interrupt、join、sleep、notify、notifyAll、wait详解
- java之sleep(),join(),yield(),wait(),notify()、notifyAll()区别
- Java并发07:Thread的基本方法(4)-Thread.sleep()、Object.wait()、notify()和notifyAll()
- java之sleep, wait, notify, notifyall
- java中wait、notify和notifyAll的概念用法和例子?
- Java线程_wait,sleep,notify,notifyall,join