在JDK的官方的wait()方法的注释中明确表示线程可能被“虚假唤醒“,JDK也明确推荐使用while来判断状态信息。那么这种情况的发生的可能性有多大呢?

  使用生产者消费者模型来说明,伪唤醒造成的后果是本来未被唤醒的线程被唤醒了,那么就破坏了生产者消费者中的判断条件,也就是例子中的while条件number == 0或者number == 1。最终导致的结果就死0和1不能交替出现。

  JDK的两种同步方案均可能出现这种伪唤醒的问题(API说明明确表示会出现这种现象),这两种组合是synchronized+wait+notify和ReentrantLock+await+signal。下面的例子中,如果把while换成if,那么就0、1就不能交替出现,反制则会,例子中是100个线程进行增加,100个线程进行减少。

  在Java并发编程书上面引用了Thinking In Java的一句话说,大概意思是:任何并发编程都是通过加锁来解决。其实JDK并发编程也是通过加锁解决,每个对象都有一个对象锁,并且有一个与这个锁相关的队列,来实现并发编程。区别在于加锁的粒度问题,读读可以并发(在读远大于写的场景下比较合适),其他三种情况不能并发。而关系型数据库也是利用这一思想,只不过做得更加彻底,除了写写不能并发,其他三种情况都能并发,这得益于MVCC模型。

public class Resource {

    public int number = 0;

    public synchronized void add() {
while (number == 1) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.err.println(number + "-" + Thread.currentThread().getId());
notifyAll();
} public synchronized void minus() {
while (number == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.err.println(number + "-" + Thread.currentThread().getId());
notifyAll();
} public static class AddThread implements Runnable { private Resource resource; public AddThread(Resource resource) {
this.resource = resource;
} @Override
public void run() {
for (;;) resource.add();
}
} public static class MinusThread implements Runnable {
private Resource resource; public MinusThread(Resource resource) {
this.resource = resource;
} @Override
public void run() {
for (;;) resource.minus();
}
} public static void main(String[] args) {
Resource resource = new Resource();
for (int i = 0; i < 100; i++) {
new Thread(new AddThread(resource)).start();
new Thread(new MinusThread(resource)).start();
}
}
}
public class ResourceLock {

    private int number = 0;

    private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition(); public int getNumber() {
return this.number;
} public void increase() {
lock.lock();
try {
while (number == 1) {
condition.await();
}
number++;
System.err.println(number + "-" + Thread.currentThread().getId());
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
} public void decrease() {
lock.lock();
try {
while (number == 0) {
condition.await();
}
number--;
System.err.println(number + "-" + Thread.currentThread().getId());
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
} static class IncreaseThread implements Runnable {
private ResourceLock resource; public IncreaseThread(ResourceLock resource) {
this.resource = resource;
} @Override
public void run() {
for (;;) resource.increase();
}
} static class DecreaseThread implements Runnable {
private ResourceLock resource; public DecreaseThread(ResourceLock resource) {
this.resource = resource;
} @Override
public void run() {
for (;;) resource.decrease();
}
} public static void main(String[] args) throws Exception {
ResourceLock resource = new ResourceLock();
ExecutorService es = Executors.newFixedThreadPool(5);
for (int i = 0; i < 100; i++) {
es.submit(new IncreaseThread(resource));
es.submit(new DecreaseThread(resource));
}
} }

谈谈JDK线程的伪唤醒的更多相关文章

  1. 多线程 - 线程通信 suspend-resume wait-notify park-unpark 伪唤醒

    线程通信(如 线程执行先后顺序,获取某个线程执行的结果等)有多种方式: 文件共享 线程1 --写入--> 文件 < --读取-- 线程2 网络共享 变量共享 线程1 --写入--> ...

  2. Java多线程系列--“基础篇”05之 线程等待与唤醒

    概要 本章,会对线程等待/唤醒方法进行介绍.涉及到的内容包括:1. wait(), notify(), notifyAll()等方法介绍2. wait()和notify()3. wait(long t ...

  3. java 多线程—— 线程等待与唤醒

    java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...

  4. Java多线程(五)——线程等待与唤醒

    一.wait().notify().notifyAll()等方法介绍 在Object.java中,定义了wait(), notify()和notifyAll()等接口.wait()的作用是让当前线程进 ...

  5. java - 线程等待与唤醒

    Java多线程系列--“基础篇”05之 线程等待与唤醒 概要 本章,会对线程等待/唤醒方法进行介绍.涉及到的内容包括:1. wait(), notify(), notifyAll()等方法介绍2. w ...

  6. java 多线程系列基础篇(五)之线程等待与唤醒

    1.wait(), notify(), notifyAll()等方法介绍 在Object.java中,定义了wait(), notify()和notifyAll()等接口.wait()的作用是让当前线 ...

  7. Java:谈谈控制线程的几种办法

    目录 Java:谈谈控制线程的几种办法 join() sleep() 守护线程 主要方法 需要注意 优先级 弃用三兄弟 stop() resume suspend 中断三兄弟 interrupt() ...

  8. jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一)

    jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一) 线程池介绍 在日常开发中经常会遇到需要使用其它线程将大量任务异步处理的场景(异步化以及提升系统的吞吐量),而在 ...

  9. jdk线程池主要原理

    本文转自:http://blog.csdn.net/linchengzhi/article/details/7567397 正常创建一个线程的时候,我们是这样的:new thread(Runnable ...

随机推荐

  1. 学习使用vim,熟悉Linux

    随着对vim一步一步的熟悉,对Linux的资料查阅,在加上今天阅读了王垠的文章,更加的觉得学习vim是正确的选择.抛弃Windows思维,进入Linux的世界,才发现是思维上的转变,如学习英语一样,是 ...

  2. Hadoop.2.x_无秘钥设置

    1.在实际生产环境中为Hadoop配置无秘钥登录非常有必要 # 在没有配置时: [liuwl@linux-66-64 hadoop-2.5.0]$ jps 26163 Jps [liuwl@linux ...

  3. 重命名PDF打印文件名

    Odoo系统默认打印出来的PDF文件都是以当前文档模型对象对应的模板文件名命名的,对用户来说,这样的命名很不友好. 我们希望能够将打印出来的文件名以单号命名,下面是实现这种目的的方法. 在report ...

  4. Vue.js基本规则提炼总结及计算属性学习

    Vue.js基本须知: 1)以“{{}}”格式 “Mustache” 语法(双大括号)来绑定表达式输出文本值; 2)以“{{{}}}”格式绑定原始的html,绑定的表达式内为字符串格式的html内容, ...

  5. 第一次scrum meeting 报告

    1.第一次scrum meeting确定了我们任务和相应的分配方案,具体分配情况如下: 这是我们团队其中一名成员的任务内容及相应预估时长,其他成员具体分配情况已在TFS上作了相应更新. 第一次scru ...

  6. Iconfont矢量图标平台全面升级

    阿里UX矢量图标库今天全新发布上线了,这次升级相对于老版本做了30多项功能和体验上的改善:赶快来体验一下吧!! 请猛戳:www.iconfont.cn 请猛戳:www.iconfont.cn 设计师p ...

  7. php课程---初学PDO

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. str_replace() 用法bug和技巧

    语法 str_replace(find,replace,string,count) 参数 描述 find 必需.规定要查找的值. replace 必需.规定替换 find 中的值的值. string ...

  9. Codeigniter CRUD生成工具

    Codeigniter  CRUD生成工具 http://crudigniter.com/

  10. Android Http请求方法汇总

    [转]  原文 这篇文章主要实现了在Android中使用JDK的HttpURLConnection和Apache的HttpClient访问网络资源,服务端采用python+flask编写,使用Serv ...