在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. MyBatis增删改查

    MyBatis的简介: MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名 ...

  2. django前端到后端一次简单完整的请求实例

    请求过程: 用户请求---〉django的路由系统---〉根据url不同分发到不同的views函数做对应处理----〉返回html格式的字符串(需要动态请求的到数据库里面拿到数据迁入到html文件中) ...

  3. MacPort 的使用

    MacPorts 的安装和使用   官网下载最版本的 安装包   自动安装  可能会出现很慢的情况   设置环境变量   vim ~/.bash_profile      i  插入修改  :wq 保 ...

  4. PowerDesigner的使用二

    PowerDesigner是一款功能非常强大的建模工具软件,足以与Rose比肩,同样是当今 最著名的建模软件之一.Rose是专攻UML对象模型的建模工具,之后才向数据库建模发展,而PowerDesig ...

  5. vue 倒计时

    简单粗暴 export default { data () { return { timer: 30, //默认倒数30秒 stop : false, //默认是停止的,但界面加载之后会变成false ...

  6. Eclipse安装jad插件进行反编译

    1.下载的eclipse是免安装的工具: 打开eclipse后在windows-preference下没有找到jadClipse,希望显示这个插件: 方法一:Help-Eclipse Marketpl ...

  7. 括号配对nyoj2(疑问)

    描述现在,有一行括号序列,请你检查这行括号是否配对.   输入 第一行输入一个数N(0<N<=100),表示有N组测试数据.后面的N行输入多组输入数据,每组输入数据都是一个字符串S(S的长 ...

  8. MyEclipse tomcat7.x 自定义项目部署路径

  9. BizTalk 开发系列(四十二) 为BizTalk应用程序打包不同的环境Binding

    我们在使用微软或者其他公司提供的BizTalk应用程序MSI包的时候经常会有一个目标环境的选择选项.该选项可以在不同的环境下使用不同的绑定(BizTalk应用程序配置)感觉很高级. 其实这个非常的简单 ...

  10. spring log4j.properties 没有日志的问题

    一.   log4j.properties 1. log4j.properties放在spring工程的src/main/rescours目录下无法读取. 测试后发现需要把log4j.properti ...