国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁技術(shù)文章
文章詳情頁

詳解Java Slipped Conditions

瀏覽:3日期:2022-08-17 18:01:51

所謂Slipped conditions,就是說, 從一個(gè)線程檢查某一特定條件到該線程操作此條件期間,這個(gè)條件已經(jīng)被其它線程改變,導(dǎo)致第一個(gè)線程在該條件上執(zhí)行了錯(cuò)誤的操作。這里有一個(gè)簡單的例子:

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()方法包含了兩個(gè)同步塊。第一個(gè)同步塊執(zhí)行wait操作直到isLocked變?yōu)閒alse才退出,第二個(gè)同步塊將isLocked置為true,以此來鎖住這個(gè)Lock實(shí)例避免其它線程通過lock()方法。

我們可以設(shè)想一下,假如在某個(gè)時(shí)刻isLocked為false, 這個(gè)時(shí)候,有兩個(gè)線程同時(shí)訪問lock方法。如果第一個(gè)線程先進(jìn)入第一個(gè)同步塊,這個(gè)時(shí)候它會(huì)發(fā)現(xiàn)isLocked為false,若此時(shí)允許第二個(gè)線程執(zhí)行,它也進(jìn)入第一個(gè)同步塊,同樣發(fā)現(xiàn)isLocked是false?,F(xiàn)在兩個(gè)線程都檢查了這個(gè)條件為false,然后它們都會(huì)繼續(xù)進(jìn)入第二個(gè)同步塊中并設(shè)置isLocked為true。

這個(gè)場(chǎng)景就是slipped conditions的例子,兩個(gè)線程檢查同一個(gè)條件, 然后退出同步塊,因此在這兩個(gè)線程改變條件之前,就允許其它線程來檢查這個(gè)條件。換句話說,條件被某個(gè)線程檢查到該條件被此線程改變期間,這個(gè)條件已經(jīng)被其它線程改變過了。

為避免slipped conditions,條件的檢查與設(shè)置必須是原子的,也就是說,在第一個(gè)線程檢查和設(shè)置條件期間,不會(huì)有其它線程檢查這個(gè)條件。

解決上面問題的方法很簡單,只是簡單的把isLocked = true這行代碼移到第一個(gè)同步塊中,放在while循環(huán)后面即可:

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(); }}

現(xiàn)在檢查和設(shè)置isLocked條件是在同一個(gè)同步塊中原子地執(zhí)行了。

一個(gè)更現(xiàn)實(shí)的例子

也許你會(huì)說,我才不可能寫這么挫的代碼,還覺得slipped conditions是個(gè)相當(dāng)理論的問題。但是第一個(gè)簡單的例子只是用來更好的展示slipped conditions。

饑餓和公平中實(shí)現(xiàn)的公平鎖也許是個(gè)更現(xiàn)實(shí)的例子。再看下嵌套管程鎖死中那個(gè)幼稚的實(shí)現(xiàn),如果我們?cè)噲D解決其中的嵌套管程鎖死問題,很容易產(chǎn)生slipped conditions問題。首先讓我們看下嵌套管程鎖死中的例子:

//Fair Lock implementation with nested monitor lockout problempublic 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(); } } }}1public class QueueObject {}

我們可以看到synchronized(queueObject)及其中的queueObject.wait()調(diào)用是嵌在synchronized(this)塊里面的,這會(huì)導(dǎo)致嵌套管程鎖死問題。為避免這個(gè)問題,我們必須將synchronized(queueObject)塊移出synchronized(this)塊。移出來之后的代碼可能是這樣的:

//Fair Lock implementation with slipped conditions problempublic 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(); } }}

注意:因?yàn)槲抑桓膭?dòng)了lock()方法,這里只展現(xiàn)了lock方法。

現(xiàn)在lock()方法包含了3個(gè)同步塊。

第一個(gè),synchronized(this)塊通過mustWait = isLocked || waitingThreads.get(0) != queueObject檢查內(nèi)部變量的值。

第二個(gè),synchronized(queueObject)塊檢查線程是否需要等待。也有可能其它線程在這個(gè)時(shí)候已經(jīng)解鎖了,但我們暫時(shí)不考慮這個(gè)問題。我們就假設(shè)這個(gè)鎖處在解鎖狀態(tài),所以線程會(huì)立馬退出synchronized(queueObject)塊。

第三個(gè),synchronized(this)塊只會(huì)在mustWait為false的時(shí)候執(zhí)行。它將isLocked重新設(shè)回true,然后離開lock()方法。

設(shè)想一下,在鎖處于解鎖狀態(tài)時(shí),如果有兩個(gè)線程同時(shí)調(diào)用lock()方法會(huì)發(fā)生什么。首先,線程1會(huì)檢查到isLocked為false,然后線程2同樣檢查到isLocked為false。接著,它們都不會(huì)等待,都會(huì)去設(shè)置isLocked為true。這就是slipped conditions的一個(gè)最好的例子。

解決Slipped Conditions問題

要解決上面例子中的slipped conditions問題,最后一個(gè)synchronized(this)塊中的代碼必須向上移到第一個(gè)同步塊中。為適應(yīng)這種變動(dòng),代碼需要做點(diǎn)小改動(dòng)。下面是改動(dòng)過的代碼:

//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; } } } } }}

我們可以看到對(duì)局部變量mustWait的檢查與賦值是在同一個(gè)同步塊中完成的。還可以看到,即使在synchronized(this)塊外面檢查了mustWait,在while(mustWait)子句中,mustWait變量從來沒有在synchronized(this)同步塊外被賦值。當(dāng)一個(gè)線程檢查到mustWait是false的時(shí)候,它將自動(dòng)設(shè)置內(nèi)部的條件(isLocked),所以其它線程再來檢查這個(gè)條件的時(shí)候,它們就會(huì)發(fā)現(xiàn)這個(gè)條件的值現(xiàn)在為true了。

synchronized(this)塊中的return;語句不是必須的。這只是個(gè)小小的優(yōu)化。如果一個(gè)線程肯定不會(huì)等待(即mustWait為false),那么就沒必要讓它進(jìn)入到synchronized(queueObject)同步塊中和執(zhí)行if(mustWait)子句了。

細(xì)心的讀者可能會(huì)注意到上面的公平鎖實(shí)現(xiàn)仍然有可能丟失信號(hào)。設(shè)想一下,當(dāng)該FairLock實(shí)例處于鎖定狀態(tài)時(shí),有個(gè)線程來調(diào)用lock()方法。執(zhí)行完第一個(gè) synchronized(this)塊后,mustWait變量的值為true。再設(shè)想一下調(diào)用lock()的線程是通過搶占式的,擁有鎖的那個(gè)線程那個(gè)線程此時(shí)調(diào)用了unlock()方法,但是看下之前的unlock()的實(shí)現(xiàn)你會(huì)發(fā)現(xiàn),它調(diào)用了queueObject.notify()。但是,因?yàn)閘ock()中的線程還沒有來得及調(diào)用queueObject.wait(),所以queueObject.notify()調(diào)用也就沒有作用了,信號(hào)就丟失掉了。如果調(diào)用lock()的線程在另一個(gè)線程調(diào)用queueObject.notify()之后調(diào)用queueObject.wait(),這個(gè)線程會(huì)一直阻塞到其它線程調(diào)用unlock方法為止,但這永遠(yuǎn)也不會(huì)發(fā)生。

公平鎖實(shí)現(xiàn)的信號(hào)丟失問題在饑餓和公平一文中我們已有過討論,把QueueObject轉(zhuǎn)變成一個(gè)信號(hào)量,并提供兩個(gè)方法:doWait()和doNotify()。這些方法會(huì)在QueueObject內(nèi)部對(duì)信號(hào)進(jìn)行存儲(chǔ)和響應(yīng)。用這種方式,即使doNotify()在doWait()之前調(diào)用,信號(hào)也不會(huì)丟失。

以上就是詳解Java Slipped Conditions的詳細(xì)內(nèi)容,更多關(guān)于Java Slipped Conditions的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 国产一区二区fc2ppv在线播放 | 狠狠色综合色综合网站久久 | 亚洲国产精品影院 | 97影院理论在线观看 | 久草视频免费在线播放 | 一级美国片免费看 | 五月天婷婷伊人 | 性欧美video另类bd | 欧美成人全部费免网站 | 亚洲国产三级在线观看 | 中文字幕在线欧美 | 亚洲天堂国产 | 99精品国产一区二区三区 | 久久欧美久久欧美精品 | 颜值超高的女神啪啪 | 国内精品久久久久久久影视麻豆 | 国产日韩在线播放 | 男人在线网址 | 国产偷自拍 | 99热成人精品热久久66 | 最近手机中文字幕1 | 91视频一区 | 在线精品国产 | 99在线观看 | 在线观看成年人免费视频 | 免费一区二区三区在线视频 | 亚洲成a人在线观看 | 国产精品一在线观看 | 日本高清一本二本三本如色坊 | 亚洲国产成人久久笫一页 | 欧美精品高清在线观看 | 国产一级内谢a级高清毛片 国产一级片毛片 | 免费的毛片 | 三级网站视频 | 欧美激情免费a视频 | 免费大片黄手机在线观看 | 99je全部都是精品视频在线 | 久在线观看 | 99热久久国产综合精品久久国产 | 天天爱天天做天天爽天天躁 | 欧美一区二区三区在线观看免费 |