java中应该尽量使用notifyall而不是notify(容易发生死锁)
2016-01-14 10:48
423 查看
比较悲剧的是我在面试的时候,搞混了,跟面试官讲反了,结果估计是留下了不好的印象......
JVM多个线程间的通信是通过 线程的锁、条件语句、以及wait()、notify()/notifyAll组成。
下面来实现一个启用多个线程来循环的输出两个不同的语句:
解释一下原因:
OutTurn类中的sub和main方法都是同步方法,所以多个调用sub和main方法的线程都会处于阻塞状态,等待一个正在运行的线程来唤醒它们。下面分别分析一下使用notify和notifyAll方法唤醒线程的不同之处:
上面的代码使用了notify方法进行唤醒,而notify方法只能唤醒一个线程,其它等待的线程仍然处于wait状态,假设调用sub方法的线程执行完后(即System. out .println("sub
---- " + count )执行完之后),所有的线程都处于等待状态,此时在sub方法中的线程执行了isSub=false语句后又执行了notify方法,这时如果唤醒的是一个sub方法的调度线程,那么while循环等于true,则此唤醒的线程也会处于等待状态,此时所有的线程都处于等待状态,那么也就没有了运行的线程来唤醒它们,这就发生了死锁。
如果使用notifyAll方法来唤醒所有正在等待该锁的线程,那么所有的线程都会处于运行前的准备状态(就是sub方法执行完后,唤醒了所有等待该锁的状态,注:不是wait状态),那么此时,即使再次唤醒一个sub方法调度线程,while循环等于true,唤醒的线程再次处于等待状态,那么还会有其它的线程可以获得锁,进入运行状态。
总结:notify方法很容易引起死锁,除非你根据自己的程序设计,确定不会发生死锁,notifyAll方法则是线程的安全唤醒方法。
附:
notify和notifyAll的区别:
notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。
void notify(): 唤醒一个正在等待该对象的线程。
void notifyAll(): 唤醒所有正在等待该对象的线程。
两者的最大区别在于:
notifyAll使所有原来在该对象上等待被notify的线程统统退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。
notify他只是选择一个wait状态线程进行通知,并使它获得该对象上的锁,但不惊动其他同样在等待被该对象notify的线程们,当第一个线程运行完毕以后释放对象上的锁,此时如果该对象没有再次使用notify语句,即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。
JVM多个线程间的通信是通过 线程的锁、条件语句、以及wait()、notify()/notifyAll组成。
下面来实现一个启用多个线程来循环的输出两个不同的语句:
[align=left]package com.tyxh.block;[/align] [align=left]class OutTurn {[/align] [align=left] private boolean isSub = true;[/align] private int count = 0; public synchronized void sub() { [align=left] try {[/align] while (!isSub ) { [align=left] this.wait();[/align] [align=left] }[/align] System. out.println("sub ---- " + count); [align=left] isSub = false ;[/align] [align=left] this.notify();[/align] } catch (Exception e) { [align=left] e.printStackTrace();[/align] [align=left] }[/align] [align=left] count++;[/align] [align=left] }[/align] public synchronized void main() { [align=left] try {[/align] while (isSub ) { [align=left] this.wait();[/align] [align=left] }[/align] System. out.println("main (((((((((((( " + count); [align=left] isSub = true ;[/align] [align=left] this.notify();[/align] } catch (Exception e) { [align=left] e.printStackTrace();[/align] [align=left] }[/align] [align=left] count++;[/align] [align=left] }[/align] [align=left]}[/align] |
[align=left]package com.tyxh.block;[/align] public class LockDemo { public static void main(String[] args) { [align=left] // System.out.println("lock");[/align] final OutTurn ot = new OutTurn(); for (int j = 0; j < 100; j++) { new Thread(new Runnable() { public void run() { [align=left] // try {[/align] [align=left] // Thread.sleep(10);[/align] // } catch (InterruptedException e) { [align=left] // e.printStackTrace();[/align] [align=left] // }[/align] for (int i = 0; i < 5; i++) { [align=left] ot.sub();[/align] [align=left] }[/align] [align=left] }[/align] [align=left] }).start();[/align] new Thread(new Runnable() { public void run() { [align=left] // try {[/align] [align=left] // Thread.sleep(10);[/align] // } catch (InterruptedException e) { [align=left] // e.printStackTrace();[/align] [align=left] // }[/align] for (int i = 0; i < 5; i++) { [align=left] ot.main();[/align] [align=left] }[/align] [align=left] }[/align] [align=left] }).start();[/align] [align=left] }[/align] [align=left] }[/align] [align=left]}[/align] |
OutTurn类中的sub和main方法都是同步方法,所以多个调用sub和main方法的线程都会处于阻塞状态,等待一个正在运行的线程来唤醒它们。下面分别分析一下使用notify和notifyAll方法唤醒线程的不同之处:
上面的代码使用了notify方法进行唤醒,而notify方法只能唤醒一个线程,其它等待的线程仍然处于wait状态,假设调用sub方法的线程执行完后(即System. out .println("sub
---- " + count )执行完之后),所有的线程都处于等待状态,此时在sub方法中的线程执行了isSub=false语句后又执行了notify方法,这时如果唤醒的是一个sub方法的调度线程,那么while循环等于true,则此唤醒的线程也会处于等待状态,此时所有的线程都处于等待状态,那么也就没有了运行的线程来唤醒它们,这就发生了死锁。
如果使用notifyAll方法来唤醒所有正在等待该锁的线程,那么所有的线程都会处于运行前的准备状态(就是sub方法执行完后,唤醒了所有等待该锁的状态,注:不是wait状态),那么此时,即使再次唤醒一个sub方法调度线程,while循环等于true,唤醒的线程再次处于等待状态,那么还会有其它的线程可以获得锁,进入运行状态。
总结:notify方法很容易引起死锁,除非你根据自己的程序设计,确定不会发生死锁,notifyAll方法则是线程的安全唤醒方法。
附:
notify和notifyAll的区别:
notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。
void notify(): 唤醒一个正在等待该对象的线程。
void notifyAll(): 唤醒所有正在等待该对象的线程。
两者的最大区别在于:
notifyAll使所有原来在该对象上等待被notify的线程统统退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。
notify他只是选择一个wait状态线程进行通知,并使它获得该对象上的锁,但不惊动其他同样在等待被该对象notify的线程们,当第一个线程运行完毕以后释放对象上的锁,此时如果该对象没有再次使用notify语句,即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。
相关文章推荐
- 每日一算法3--JAVA时间格式化处理
- spring 的MAVEN配置
- Eclipse中Java项目的红色感叹号和黄色盾牌感叹号
- 在Springmvc中导出报表下载Excel文件
- java配置数据库连接池的方法步骤
- Eclipse插件的安装方法三则
- java 验证码 基于servlet+jsp
- Linkedin工程师是如何优化他们的Java代码的
- 来学java吧
- Java图形化界面设计——容器(JFrame)
- Java中BigDecimal的8种舍入模式
- java notify wait
- 用java解析在OpenStreetMap上下载的地图数据(SAX版,适合比较大的xml文件)
- eclipse常用快捷键,这个只要新学会的常用的会陆续更新的。
- java160108TicketDemo2
- java160108TicketDemo
- java160108ThisLockDemo
- struts2常用标签总结
- JAVA设计模式之单例模式
- java160108StaticMethodDemo