谈谈JDK线程的伪唤醒
在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线程的伪唤醒的更多相关文章
- 多线程 - 线程通信 suspend-resume wait-notify park-unpark 伪唤醒
线程通信(如 线程执行先后顺序,获取某个线程执行的结果等)有多种方式: 文件共享 线程1 --写入--> 文件 < --读取-- 线程2 网络共享 变量共享 线程1 --写入--> ...
- Java多线程系列--“基础篇”05之 线程等待与唤醒
概要 本章,会对线程等待/唤醒方法进行介绍.涉及到的内容包括:1. wait(), notify(), notifyAll()等方法介绍2. wait()和notify()3. wait(long t ...
- java 多线程—— 线程等待与唤醒
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- Java多线程(五)——线程等待与唤醒
一.wait().notify().notifyAll()等方法介绍 在Object.java中,定义了wait(), notify()和notifyAll()等接口.wait()的作用是让当前线程进 ...
- java - 线程等待与唤醒
Java多线程系列--“基础篇”05之 线程等待与唤醒 概要 本章,会对线程等待/唤醒方法进行介绍.涉及到的内容包括:1. wait(), notify(), notifyAll()等方法介绍2. w ...
- java 多线程系列基础篇(五)之线程等待与唤醒
1.wait(), notify(), notifyAll()等方法介绍 在Object.java中,定义了wait(), notify()和notifyAll()等接口.wait()的作用是让当前线 ...
- Java:谈谈控制线程的几种办法
目录 Java:谈谈控制线程的几种办法 join() sleep() 守护线程 主要方法 需要注意 优先级 弃用三兄弟 stop() resume suspend 中断三兄弟 interrupt() ...
- jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一)
jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一) 线程池介绍 在日常开发中经常会遇到需要使用其它线程将大量任务异步处理的场景(异步化以及提升系统的吞吐量),而在 ...
- jdk线程池主要原理
本文转自:http://blog.csdn.net/linchengzhi/article/details/7567397 正常创建一个线程的时候,我们是这样的:new thread(Runnable ...
随机推荐
- [LintCode] Backpack VI 背包之六
Given an integer array nums with all positive numbers and no duplicates, find the number of possible ...
- 1021上课演练----SQL注入与避免(银行系统)
package com.bank; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.Dat ...
- CMD和DOS的区别
很多人喜欢在这里把XP下的CMD和我们的DOS混淆,因此今天我特别发帖说明:CMD和DOS是完全不同的!请各位始终牢记这一点,否则在高手面前就丢人了!因为CMD是32位应用程序(而DOS是16位操作系 ...
- SQL 参数化查询 应用于 Like
在sql 进行参数化查询的时候,使用like 语句和参数的时候,错误的写法: Participant like '%@Participant%' ,这样在数据库为解析为 '%'participant ...
- Javascript 中的一些关于时间的操作【转】
1.时间对象和一些简单操作函数 var myDate = new Date(); myDate.getYear(); //获取当前年份(2位) myDate.getFullYear(); ...
- Ubuntu Linux上安装配置Mysql
一.安装: 三种安装方式: 1. 从网上安装 sudo apt-get install mysql-server.装完已经自动配置好环境变量,可以直接使用mysql的命令. 注:建议将/etc/apt ...
- 【iCore3 双核心板_ uC/OS-III】例程十一:任务消息队列
实验指导书及代码包下载: http://pan.baidu.com/s/1pLQYiE3 iCore3 购买链接: https://item.taobao.com/item.htm?id=524229 ...
- JS写的CRC16校验算法
var CRC = {}; CRC.CRC16 = function (data) { var len = data.length; if (len > 0) { var crc = 0xFFF ...
- 关于Matrix的深入理解(对应值的功能)
matrix是css3里面transform的一个集成表达式,它是以一个矩阵的方式来计算 transform:translate(apx,bpx)位移 transform: scale(a,b) 放缩 ...
- KeyValuePair
KeyValuePair用法(转)(2012-06-25 10:47:35) 转载▼ // 标签: keyvaluepair it KeyValuePair C# KeyValuePair< ...