- 浏览: 1898581 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
July01:
最近了解到一款StratoIO打印控件,功能如下:1、Html ...
jquery打印指定的div -
GentlemanQc:
...
quartz系列(二)spring3.2.5与quartz2.1.7集群版集成简要说明 -
静夜独窗:
你好,能说一下server.xml增加的配置是怎么影响性能的吗 ...
tomcat7.0性能优化-挑战极限精简版 -
beyondfengyu:
beyondfengyu 写道如果每个客户进程的时间不同步,时 ...
java并发(二十二)分布式锁 -
beyondfengyu:
如果每个客户进程的时间不同步,时间超前的进程是不是更容易得到锁 ...
java并发(二十二)分布式锁
所谓Slipped conditions,就是说, 从一个线程检查某一特定条件到该线程操作此条件期间,这个条件已经被其它线程改变,导致第一个线程在该条件上执行了错误的操作。这里有一个简单的例子:
我们可以看到,lock()方法包含了两个同步块。第一个同步块执行wait操作直到isLocked变为false才退出,第二个同步块将isLocked置为true,以此来锁住这个Lock实例避免其它线程通过lock()方法。
我们可以设想一下,假如在某个时刻isLocked为false, 这个时候,有两个线程同时访问lock方法。如果第一个线程先进入第一个同步块,这个时候它会发现isLocked为false,若此时允许第二个线程执行,它也进入第一个同步块,同样发现isLocked是false。现在两个线程都检查了这个条件为false,然后它们都会继续进入第二个同步块中并设置isLocked为true。
这个场景就是slipped conditions的例子,两个线程检查同一个条件, 然后退出同步块,因此在这两个线程改变条件之前,就允许其它线程来检查这个条件。换句话说,条件被某个线程检查到该条件被此线程改变期间,这个条件已经被其它线程改变过了。
为避免slipped conditions,条件的检查与设置必须是原子的,也就是说,在第一个线程检查和设置条件期间,不会有其它线程检查这个条件。
解决上面问题的方法很简单,只是简单的把isLocked = true这行代码移到第一个同步块中,放在while循环后面即可:
现在检查和设置isLocked条件是在同一个同步块中原子地执行了。
一个更现实的例子
也许你会说,我才不可能写这么挫的代码,还觉得slipped conditions是个相当理论的问题。但是第一个简单的例子只是用来更好的展示slipped conditions。
饥饿和公平中实现的公平锁也许是个更现实的例子。再看下嵌套管程锁死中那个幼稚的实现,如果我们试图解决其中的嵌套管程锁死问题,很容易产生slipped conditions问题。 首先让我们看下嵌套管程锁死中的例子:
我们可以看到synchronized(queueObject)及其中的queueObject.wait()调用是嵌在synchronized(this)块里面的,这会导致嵌套管程锁死问题。为避免这个问题,我们必须将synchronized(queueObject)块移出synchronized(this)块。移出来之后的代码可能是这样的:
注意:因为我只改动了lock()方法,这里只展现了lock方法。
现在lock()方法包含了3个同步块。
第一个,synchronized(this)块通过mustWait = isLocked || waitingThreads.get(0) != queueObject检查内部变量的值。
第二个,synchronized(queueObject)块检查线程是否需要等待。也有可能其它线程在这个时候已经解锁了,但我们暂时不考虑这个问题。我们就假设这个锁处在解锁状态,所以线程会立马退出synchronized(queueObject)块。
第三个,synchronized(this)块只会在mustWait为false的时候执行。它将isLocked重新设回true,然后离开lock()方法。
设想一下,在锁处于解锁状态时,如果有两个线程同时调用lock()方法会发生什么。首先,线程1会检查到isLocked为false,然后线程2同样检查到isLocked为false。接着,它们都不会等待,都会去设置isLocked为true。这就是slipped conditions的一个最好的例子。
解决Slipped Conditions问题
要解决上面例子中的slipped conditions问题,最后一个synchronized(this)块中的代码必须向上移到第一个同步块中。为适应这种变动,代码需要做点小改动。下面是改动过的代码:
我们可以看到对局部变量mustWait的检查与赋值是在同一个同步块中完成的。还可以看到,即使在synchronized(this)块外面检查了mustWait,在while(mustWait)子句中,mustWait变量从来没有在synchronized(this)同步块外被赋值。当一个线程检查到mustWait是false的时候,它将自动设置内部的条件(isLocked),所以其它线程再来检查这个条件的时候,它们就会发现这个条件的值现在为true了。
synchronized(this)块中的return;语句不是必须的。这只是个小小的优化。如果一个线程肯定不会等待(即mustWait为false),那么就没必要让它进入到synchronized(queueObject)同步块中和执行if(mustWait)子句了。
细心的读者可能会注意到上面的公平锁实现仍然有可能丢失信号。设想一下,当该FairLock实例处于锁定状态时,有个线程来调用lock()方法。执行完第一个 synchronized(this)块后,mustWait变量的值为true。再设想一下调用lock()的线程是通过抢占式的,拥有锁的那个线程那个线程此时调用了unlock()方法,但是看下之前的unlock()的实现你会发现,它调用了queueObject.notify()。但是,因为lock()中的线程还没有来得及调用queueObject.wait(),所以queueObject.notify()调用也就没有作用了,信号就丢失掉了。如果调用lock()的线程在另一个线程调用queueObject.notify()之后调用queueObject.wait(),这个线程会一直阻塞到其它线程调用unlock方法为止,但这永远也不会发生。
公平锁实现的信号丢失问题在饥饿和公平一文中我们已有过讨论,把QueueObject转变成一个信号量,并提供两个方法:doWait()和doNotify()。这些方法会在QueueObject内部对信号进行存储和响应。用这种方式,即使doNotify()在doWait()之前调用,信号也不会丢失。
public class Lock { private boolean isLocked = true; public void lock() { synchronized (this) { while (isLocked) { try { this.wait(); } catch (InterruptedException e) { // do nothing, keep waiting } } } synchronized (this) { isLocked = true; } } public synchronized void unlock() { isLocked = false; this.notify(); } }
我们可以看到,lock()方法包含了两个同步块。第一个同步块执行wait操作直到isLocked变为false才退出,第二个同步块将isLocked置为true,以此来锁住这个Lock实例避免其它线程通过lock()方法。
我们可以设想一下,假如在某个时刻isLocked为false, 这个时候,有两个线程同时访问lock方法。如果第一个线程先进入第一个同步块,这个时候它会发现isLocked为false,若此时允许第二个线程执行,它也进入第一个同步块,同样发现isLocked是false。现在两个线程都检查了这个条件为false,然后它们都会继续进入第二个同步块中并设置isLocked为true。
这个场景就是slipped conditions的例子,两个线程检查同一个条件, 然后退出同步块,因此在这两个线程改变条件之前,就允许其它线程来检查这个条件。换句话说,条件被某个线程检查到该条件被此线程改变期间,这个条件已经被其它线程改变过了。
为避免slipped conditions,条件的检查与设置必须是原子的,也就是说,在第一个线程检查和设置条件期间,不会有其它线程检查这个条件。
解决上面问题的方法很简单,只是简单的把isLocked = true这行代码移到第一个同步块中,放在while循环后面即可:
public class Lock { private boolean isLocked = true; public void lock() { synchronized (this) { while (isLocked) { try { this.wait(); } catch (InterruptedException e) { // do nothing, keep waiting } } isLocked = true; } } public synchronized void unlock() { isLocked = false; this.notify(); } }
现在检查和设置isLocked条件是在同一个同步块中原子地执行了。
一个更现实的例子
也许你会说,我才不可能写这么挫的代码,还觉得slipped conditions是个相当理论的问题。但是第一个简单的例子只是用来更好的展示slipped conditions。
饥饿和公平中实现的公平锁也许是个更现实的例子。再看下嵌套管程锁死中那个幼稚的实现,如果我们试图解决其中的嵌套管程锁死问题,很容易产生slipped conditions问题。 首先让我们看下嵌套管程锁死中的例子:
//Fair Lock implementation with nested monitor lockout problem public class FairLock { private boolean isLocked = false; private Thread lockingThread = null; private List waitingThreads = new ArrayList(); public void lock() throws InterruptedException{ QueueObject queueObject = new QueueObject(); synchronized(this){ waitingThreads.add(queueObject); while(isLocked || waitingThreads.get(0) != queueObject){ synchronized(queueObject){ try{ queueObject.wait(); }catch(InterruptedException e){ waitingThreads.remove(queueObject); throw e; } } } waitingThreads.remove(queueObject); isLocked = true; lockingThread = Thread.currentThread(); } } public synchronized void unlock(){ if(this.lockingThread != Thread.currentThread()){ throw new IllegalMonitorStateException("Calling thread has not locked this lock"); } isLocked = false; lockingThread = null; if(waitingThreads.size() > 0){ QueueObject queueObject = waitingThread.get(0); synchronized(queueObject){ queueObject.notify(); } } } } public class QueueObject {}
我们可以看到synchronized(queueObject)及其中的queueObject.wait()调用是嵌在synchronized(this)块里面的,这会导致嵌套管程锁死问题。为避免这个问题,我们必须将synchronized(queueObject)块移出synchronized(this)块。移出来之后的代码可能是这样的:
//Fair Lock implementation with slipped conditions problem public class FairLock { private boolean isLocked = false; private Thread lockingThread = null; private List waitingThreads = new ArrayList(); public void lock() throws InterruptedException{ QueueObject queueObject = new QueueObject(); synchronized(this){ waitingThreads.add(queueObject); } boolean mustWait = true; while(mustWait){ synchronized(this){ mustWait = isLocked || waitingThreads.get(0) != queueObject; } synchronized(queueObject){ if(mustWait){ try{ queueObject.wait(); }catch(InterruptedException e){ waitingThreads.remove(queueObject); throw e; } } } } synchronized(this){ waitingThreads.remove(queueObject); isLocked = true; lockingThread = Thread.currentThread(); } } }
注意:因为我只改动了lock()方法,这里只展现了lock方法。
现在lock()方法包含了3个同步块。
第一个,synchronized(this)块通过mustWait = isLocked || waitingThreads.get(0) != queueObject检查内部变量的值。
第二个,synchronized(queueObject)块检查线程是否需要等待。也有可能其它线程在这个时候已经解锁了,但我们暂时不考虑这个问题。我们就假设这个锁处在解锁状态,所以线程会立马退出synchronized(queueObject)块。
第三个,synchronized(this)块只会在mustWait为false的时候执行。它将isLocked重新设回true,然后离开lock()方法。
设想一下,在锁处于解锁状态时,如果有两个线程同时调用lock()方法会发生什么。首先,线程1会检查到isLocked为false,然后线程2同样检查到isLocked为false。接着,它们都不会等待,都会去设置isLocked为true。这就是slipped conditions的一个最好的例子。
解决Slipped Conditions问题
要解决上面例子中的slipped conditions问题,最后一个synchronized(this)块中的代码必须向上移到第一个同步块中。为适应这种变动,代码需要做点小改动。下面是改动过的代码:
//Fair Lock implementation without nested monitor lockout problem, //but with missed signals problem. public class FairLock { private boolean isLocked = false; private Thread lockingThread = null; private List waitingThreads = new ArrayList(); public void lock() throws InterruptedException{ QueueObject queueObject = new QueueObject(); synchronized(this){ waitingThreads.add(queueObject); } boolean mustWait = true; while(mustWait){ synchronized(this){ mustWait = isLocked || waitingThreads.get(0) != queueObject; if(!mustWait){ waitingThreads.remove(queueObject); isLocked = true; lockingThread = Thread.currentThread(); return; } } synchronized(queueObject){ if(mustWait){ try{ queueObject.wait(); }catch(InterruptedException e){ waitingThreads.remove(queueObject); throw e; } } } } } }
我们可以看到对局部变量mustWait的检查与赋值是在同一个同步块中完成的。还可以看到,即使在synchronized(this)块外面检查了mustWait,在while(mustWait)子句中,mustWait变量从来没有在synchronized(this)同步块外被赋值。当一个线程检查到mustWait是false的时候,它将自动设置内部的条件(isLocked),所以其它线程再来检查这个条件的时候,它们就会发现这个条件的值现在为true了。
synchronized(this)块中的return;语句不是必须的。这只是个小小的优化。如果一个线程肯定不会等待(即mustWait为false),那么就没必要让它进入到synchronized(queueObject)同步块中和执行if(mustWait)子句了。
细心的读者可能会注意到上面的公平锁实现仍然有可能丢失信号。设想一下,当该FairLock实例处于锁定状态时,有个线程来调用lock()方法。执行完第一个 synchronized(this)块后,mustWait变量的值为true。再设想一下调用lock()的线程是通过抢占式的,拥有锁的那个线程那个线程此时调用了unlock()方法,但是看下之前的unlock()的实现你会发现,它调用了queueObject.notify()。但是,因为lock()中的线程还没有来得及调用queueObject.wait(),所以queueObject.notify()调用也就没有作用了,信号就丢失掉了。如果调用lock()的线程在另一个线程调用queueObject.notify()之后调用queueObject.wait(),这个线程会一直阻塞到其它线程调用unlock方法为止,但这永远也不会发生。
公平锁实现的信号丢失问题在饥饿和公平一文中我们已有过讨论,把QueueObject转变成一个信号量,并提供两个方法:doWait()和doNotify()。这些方法会在QueueObject内部对信号进行存储和响应。用这种方式,即使doNotify()在doWait()之前调用,信号也不会丢失。
发表评论
-
java并发(三十四)协程kilim
2015-10-02 11:29 5647概述 对协程的技术已经觊觎很久,他有高性能的优点,但目前工具对 ... -
java并发(三十三)栅栏CyclicBarrier
2015-09-12 16:09 3381如果说CountDownLatch(闭锁)是一次性的, ... -
java并发(三十二)非阻塞算法
2014-06-25 14:08 1338如果在某算法中,一个线程的失败或挂起不会导致其他线程也 ... -
java并发(三十一)Amdahl定律
2014-05-19 16:15 2224阿姆达尔定律 阿姆达尔(Amdahl)定律是计算机系统设计的重 ... -
java并发(三十)闭锁CountDownLatch
2014-05-07 23:33 906CountDownLatch,一个同步辅助类,在完成一组正在其 ... -
java并发(二十九)构建高效且可伸缩的结果缓存
2014-04-23 11:52 1091概述 几乎所有应用程序,都会使用某种形式的缓存。重用之 ... -
java并发(二十八)并发随机数,原子变量,并发集合
2014-04-13 12:04 4090原子变量 java.util.concurrent.a ... -
java并发(二十七) 并发性标注
2014-04-07 10:49 8992一介绍 <dependency> < ... -
java并发(二十六)正确使用Volatile变量
2014-03-30 19:41 1399概述 您只能在有限的一 ... -
java并发(二十五)java7之fork-join框架
2014-03-26 14:12 11516如果让我个人理解什么 ... -
java并发(二十四)多线程结果组装
2014-03-24 22:01 3947本文主要介绍多线程的 ... -
java并发(二十三)阻塞、非阻塞、同步、异步
2014-03-14 17:01 1449因为中文语意的问题, ... -
java并发(二十二)分布式锁
2014-03-12 16:24 18735Redis有一系列的命令, ... -
java并发(二十一)剖析同步器
2014-03-11 18:03 1204虽然许多同步器(如锁,信号量,阻塞队列等)功能上各不相同,但它 ... -
java并发(二十)线程池
2014-03-10 15:02 3935基本介绍 线程池(Thread Pool)对于限制应用程序中同 ... -
java并发(十九)阻塞队列
2014-03-10 14:46 1353阻塞队列与普通队列的 ... -
java并发(十八)信号量
2014-03-10 14:03 1281Semaphore(信号量) 是一个线程同步结构,用于在线程间 ... -
java并发(十七)重入锁死
2014-03-10 11:33 1137重入锁死与死锁和嵌套管程锁死非常相似。当一个线程重新获取锁,读 ... -
java并发(十六)Java中的读/写锁
2014-03-10 10:17 1230相比Java中的锁(Locks in Ja ... -
java并发(十五)Java中的锁
2014-03-09 12:07 957锁像synchronized同步块一样,是一种线程同步机制,但 ...
相关推荐
在计算机科学和并发编程领域,"哲学家进餐问题"(Dining Philosophers Problem)是一个经典的示例,用来揭示资源竞争和死锁的可能性。这个问题由埃德s伽莫夫首次提出,后来被艾兹格迪杰斯特拉进一步阐述,用以解释多...
双线性插值是一种常用的高质量放大方法,它通过四个相邻像素的加权平均来计算新位置的像素值。 3. **定义视口和设备上下文**:在VC++中,我们使用`CDC`(Device Context)类来管理图形设备,如屏幕或打印机。我们...
标题 "lmd.rar_decomposition_lmd_mode decomposition_should5nl_slipped6e" 暗示了我们正在讨论一种名为“LMD”(Lagrange Multiplier Decomposition)的方法,这可能是一个信号处理或数据分析的技术,用于分解复杂...
打开工程找到下属的mitk这个工程编译。出现两个警告和错误。打开出现warning的文件(mitkPointSetGLMapper2D和mitk:SlicedGeometry3D),Ctrl+A全选,然后在文件菜单:file->advanced save options ,在弹出的选项...
人脸识别,精简,利用框架和几个点识别人脸,快速上手
基于51单片机的iCAN协议程序,是一个很好的例子
Got absolutely slammed in an interview [Java]。Also interesting was they were completely disinterested in anything I had made or written, it was entirely basic functionality knowledge that I slipped ...
英语四级高1.alter v. 改变,改动,变更 I'll have to alter the diagram. I've made a mistake 2.burst vi.,n. 突然发生,爆裂 The police burst through the door 3.dispose vi. 除掉;处置;解决;处理(of) I ...
- 例句:He slipped on the wet floor and fell down.(他在湿滑的地面上滑了一跤。) 10. **slide** (v.) 滑动,滑落 (n.) 滑动;滑面;幻灯片 - 例句:The children were sliding down the grassy hill.(孩子...
大学英语四级考试是中国高等教育自学考试委员会组织的一项标准化英语水平考试,旨在测试非英语专业的大学生是否具备完成基础英语学习和交流的能力。四级考试内容包括听力理解、阅读理解、翻译和写作四个部分。其中,...
例句:He slipped on the icy road and fell. 10. slide v. 滑动,滑落 n. 滑动;滑面;幻灯片:常用于描述平滑的移动或幻灯片展示。 例句:The children enjoyed sliding down the hill on their sleds. 11. ...
【英语核心700词及例句】是针对学习者设计的一个词汇强化工具,特别适合备考新大学英语四级710分制的学生。这个资源包含了700个关键的英语单词,每个单词都配有生动的例句,帮助学习者更好地理解和记忆。 1. Alter...
This document is primarily aimed at particle swarm optimization (pso) algorithm in matlab application said write m files that have slipped into the environment
英语四级核心高频词汇是备考四级考试的关键,这些词汇涵盖了各个主题和语境,包括日常生活、科学、文化等多个领域。以下是一些重点词汇及其详细解释: 1. alter:改变,改动,变更 - 表示对事物进行修改或调整,如...
第七题"It is reported that the 60% discounts on some Beijing-Shanghai flights have now slipped back to 20% one day in advance." 句子中省略了when they are,表示在提前一天预订时,折扣回到了20%。...
- **例句**:The company decided to alter its business strategy in response to the changing market conditions. - **解析**:alter通常用于指对原有计划、设计或状态做出修改,强调的是“改变”这一动作。 ###...
例句: She slipped on the icy road and fell. 10. slide v. 滑动,滑落 n. 滑动;滑面;幻灯片 - 描述平滑移动或演示中的幻灯片。 例句: The children enjoyed sliding down the hill on sleds. 11. bacteria n. ...
- *例句*: He slipped on the ice and fell down. (他在冰上滑了一跤,摔倒了。) 10. **slide v. 滑动;n. 幻灯片;滑动** - *含义*: 物体沿表面移动,通常不受控制。 - *例句*: The book slid off the table. ...
忽略):指物体或人的滑动,也可表示不经意的错误,如:I slipped on the ice and fell. 10. **slide** (v. 滑动,滑落 n. 滑动;滑面;幻灯片):滑行或展示的幻灯片,如:The child slid down the slide in the ...
这份文档是针对英语四级考试中出现频率较高的词汇进行的整理,涵盖了多个领域的词汇,对于备考四级的考生来说是非常有价值的参考资料。以下将详细解释其中的一些重要词汇及其应用: 1. alter:改变,改动。例如:...