在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. Qt 控件随窗口缩放

    在Qt的界面设计中,我们有时候希望窗口在最大化的时候,上面的控件也跟着缩放,那么我们就需要调整控件的SizePolicy属性,关于这个属性的讲解请参见我之前的博客Qt SizePolicy 属性,由于 ...

  2. [zt]java synchronized详解

    作者:GangWang 出处:http://www.cnblogs.com/GnagWang/ 记下来,很重要. Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多 ...

  3. ado.net 修改,查询

    修改: using System; using System.Collections.Generic; using System.Linq; using System.Text; using Syst ...

  4. mysqldump备份详解

    -A  备份所有-B  恢复时会自动创建库  (同时支持导出多个库  -B  db01 db02) -d  导出表结构 #库中有多个表导出时导出没加 –B参数,则要先导入结构,如果表结构没有备份,那就 ...

  5. javascript 变量,作用域,内存管理小结

    js的变量保存两种类型的数据——基本数据类型与引用类型.具有以下几点特征:   变量: 1)基本类型值在内存中占固定大小的空间,因此被保存在栈内存中; 2) 把保存基本类型值得变量赋给另一个变量,会创 ...

  6. FZU1894 单调队列

    S - 1019 Time Limit:1500MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Sta ...

  7. spring security使用数据库资源

    国内对权限系统的基本要求是将用户权限和被保护资源都放在数据库里进行管理,在这点上Spring Security并没有给出官方的解决方案,为此我们需要对Spring Security进行扩展.. 数据库 ...

  8. [转]linux 查看系统信息命令

    linux 查看系统信息命令是linux初学者必备的基础知识, 这些命令也非常有用, 因为进入linux第一件事就可能是首先查看系统信息, 因此必要的系统的学习一下这些linux系统信息命令还是非常有 ...

  9. linux一些名词

    硬链接: 同一个索引节点的两个文件 ln a b //创建a的连接文件b,删除a,不失效软连接: 不同索引节点,相当于一个快捷方式ln -s a b,删除a,b失效 网络协议: 网络协议就是通信规则的 ...

  10. Oracle 11g的Redo Log和Archive Log的分析方法

    自Oracle 11g起,无需设置UTL_FILE_DIR就可以使用LOGMNR对本地数据库的日志进行分析,以下是使用LOGMNR的DICT_FROM_ONLINE_CATALOG分析REDO和归档日 ...