日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

18、Java并发性和多线程-饥饿与公平

發(fā)布時(shí)間:2023/11/29 java 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 18、Java并发性和多线程-饥饿与公平 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

以下內(nèi)容轉(zhuǎn)自http://ifeve.com/starvation-and-fairness/:

如果一個(gè)線程因?yàn)镃PU時(shí)間全部被其他線程搶走而得不到CPU運(yùn)行時(shí)間,這種狀態(tài)被稱(chēng)之為“饑餓”。而該線程被“饑餓致死”正是因?yàn)樗貌坏紺PU運(yùn)行時(shí)間的機(jī)會(huì)。解決饑餓的方案被稱(chēng)之為“公平性”–即所有線程均能公平地獲得運(yùn)行機(jī)會(huì)。

?下面是本文討論的主題:

1.?Java中導(dǎo)致饑餓的原因:

  • 高優(yōu)先級(jí)線程吞噬所有的低優(yōu)先級(jí)線程的CPU時(shí)間。
  • 線程被永久堵塞在一個(gè)等待進(jìn)入同步塊的狀態(tài)。
  • 線程在等待一個(gè)本身也處于永久等待完成的對(duì)象(比如調(diào)用這個(gè)對(duì)象的wait方法)。

2.?在Java中實(shí)現(xiàn)公平性方案,需要:

  • 使用鎖,而不是同步塊。
  • 公平鎖。
  • 注意性能方面。

Java中導(dǎo)致饑餓的原因

在Java中,下面三個(gè)常見(jiàn)的原因會(huì)導(dǎo)致線程饑餓:

  • 高優(yōu)先級(jí)線程吞噬所有的低優(yōu)先級(jí)線程的CPU時(shí)間。
  • 線程被永久堵塞在一個(gè)等待進(jìn)入同步塊的狀態(tài),因?yàn)槠渌€程總是能在它之前持續(xù)地對(duì)該同步塊進(jìn)行訪問(wèn)。
  • 線程在等待一個(gè)本身(在其上調(diào)用wait())也處于永久等待完成的對(duì)象,因?yàn)槠渌€程總是被持續(xù)地獲得喚醒。
  • 高優(yōu)先級(jí)線程吞噬所有的低優(yōu)先級(jí)線程的CPU時(shí)間

    你能為每個(gè)線程設(shè)置獨(dú)自的線程優(yōu)先級(jí),優(yōu)先級(jí)越高的線程獲得的CPU時(shí)間越多,線程優(yōu)先級(jí)值設(shè)置在1到10之間,而這些優(yōu)先級(jí)值所表示行為的準(zhǔn)確解釋則依賴(lài)于你的應(yīng)用運(yùn)行平臺(tái)。對(duì)大多數(shù)應(yīng)用來(lái)說(shuō),你最好是不要改變其優(yōu)先級(jí)值。

    線程被永久堵塞在一個(gè)等待進(jìn)入同步塊的狀態(tài)

    Java的同步代碼區(qū)也是一個(gè)導(dǎo)致饑餓的因素。Java的同步代碼區(qū)對(duì)哪個(gè)線程允許進(jìn)入的次序沒(méi)有任何保障。這就意味著理論上存在一個(gè)試圖進(jìn)入該同步區(qū)的線程處于被永久堵塞的風(fēng)險(xiǎn),因?yàn)槠渌€程總是能持續(xù)地先于它獲得訪問(wèn),這即是“饑餓”問(wèn)題,而一個(gè)線程被“饑餓致死”正是因?yàn)樗貌坏紺PU運(yùn)行時(shí)間的機(jī)會(huì)。

    線程在等待一個(gè)本身(在其上調(diào)用wait())也處于永久等待完成的對(duì)象

    如果多個(gè)線程處在wait()方法執(zhí)行上,而對(duì)其調(diào)用notify()不會(huì)保證哪一個(gè)線程會(huì)獲得喚醒,任何線程都有可能處于繼續(xù)等待的狀態(tài)。因此存在這樣一個(gè)風(fēng)險(xiǎn):一個(gè)等待線程從來(lái)得不到喚醒,因?yàn)槠渌却€程總是能被獲得喚醒。

    在Java中實(shí)現(xiàn)公平性

    雖Java不可能實(shí)現(xiàn)100%的公平性,我們依然可以通過(guò)同步結(jié)構(gòu)在線程間實(shí)現(xiàn)公平性的提高。

    首先來(lái)學(xué)習(xí)一段簡(jiǎn)單的同步態(tài)代碼:

    public class Synchronizer{public synchronized void doSynchronized(){//do a lot of work which takes a long time } }

    如果有一個(gè)以上的線程調(diào)用doSynchronized()方法,在第一個(gè)獲得訪問(wèn)的線程未完成前,其他線程將一直處于阻塞狀態(tài),而且在這種多線程被阻塞的場(chǎng)景下,接下來(lái)將是哪個(gè)線程獲得訪問(wèn)是沒(méi)有保障的。

    使用鎖方式替代同步塊

    為了提高等待線程的公平性,我們使用鎖方式來(lái)替代同步塊。

    public class Synchronizer{Lock lock = new Lock();public void doSynchronized() throws InterruptedException{this.lock.lock();//critical section, do a lot of work which takes a long timethis.lock.unlock();} }

    注意到doSynchronized()不再聲明為synchronized,而是用lock.lock()和lock.unlock()來(lái)替代。

    下面是用Lock類(lèi)做的一個(gè)實(shí)現(xiàn):

    public class Lock {private boolean isLocked = false;private Thread lockingThread = null;public synchronized void lock() throws InterruptedException {while (isLocked) {wait();}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;notify();} }

    注意到上面對(duì)Lock的實(shí)現(xiàn),如果存在多線程并發(fā)訪問(wèn)lock(),這些線程將阻塞在對(duì)lock()方法的訪問(wèn)上。另外,如果鎖已經(jīng)鎖上(校對(duì)注:這里指的是isLocked等于true時(shí)),這些線程將阻塞在while(isLocked)循環(huán)的wait()調(diào)用里面。要記住的是,當(dāng)線程正在等待進(jìn)入lock() 時(shí),可以調(diào)用wait()釋放其鎖實(shí)例對(duì)應(yīng)的同步鎖,使得其他多個(gè)線程可以進(jìn)入lock()方法,并調(diào)用wait()方法。

    這回看下doSynchronized(),你會(huì)注意到在lock()和unlock()之間的注釋:在這兩個(gè)調(diào)用之間的代碼將運(yùn)行很長(zhǎng)一段時(shí)間。進(jìn)一步設(shè)想,這段代碼將長(zhǎng)時(shí)間運(yùn)行,和進(jìn)入lock()并調(diào)用wait()來(lái)比較的話。這意味著大部分時(shí)間用在等待進(jìn)入鎖和進(jìn)入臨界區(qū)的過(guò)程是用在wait()的等待中,而不是被阻塞在試圖進(jìn)入lock()方法中。

    在早些時(shí)候提到過(guò),同步塊不會(huì)對(duì)等待進(jìn)入的多個(gè)線程誰(shuí)能獲得訪問(wèn)做任何保障,同樣當(dāng)調(diào)用notify()時(shí),wait()也不會(huì)做保障一定能喚醒線程(至于為什么,請(qǐng)看線程通信)。因此這個(gè)版本的Lock類(lèi)和doSynchronized()那個(gè)版本就保障公平性而言,沒(méi)有任何區(qū)別。

    但我們能改變這種情況。當(dāng)前的Lock類(lèi)版本調(diào)用自己的wait()方法,如果每個(gè)線程在不同的對(duì)象上調(diào)用wait(),那么只有一個(gè)線程會(huì)在該對(duì)象上調(diào)用wait(),Lock類(lèi)可以決定哪個(gè)對(duì)象能對(duì)其調(diào)用notify(),因此能做到有效的選擇喚醒哪個(gè)線程。

    公平鎖

    下面來(lái)講述將上面Lock類(lèi)轉(zhuǎn)變?yōu)楣芥iFairLock。你會(huì)注意到新的實(shí)現(xiàn)和之前的Lock類(lèi)中的同步和wait()/notify()稍有不同。

    準(zhǔn)確地說(shuō)如何從之前的Lock類(lèi)做到公平鎖的設(shè)計(jì)是一個(gè)漸進(jìn)設(shè)計(jì)的過(guò)程,每一步都是在解決上一步的問(wèn)題而前進(jìn)的:Nested Monitor Lockout, Slipped Conditions和Missed Signals。這些本身的討論雖已超出本文的范圍,但其中每一步的內(nèi)容都將會(huì)專(zhuān)題進(jìn)行討論。重要的是,每一個(gè)調(diào)用lock()的線程都會(huì)進(jìn)入一個(gè)隊(duì)列,當(dāng)解鎖后,只有隊(duì)列里的第一個(gè)線程被允許鎖住Farlock實(shí)例,所有其它的線程都將處于等待狀態(tài),直到他們處于隊(duì)列頭部。

    public class FairLock {private boolean isLocked = false;private Thread lockingThread = null;private List<QueueObject> waitingThreads = new ArrayList<QueueObject>();public void lock() throws InterruptedException {QueueObject queueObject = new QueueObject();boolean isLockedForThisThread = true;synchronized (this) {waitingThreads.add(queueObject);}while (isLockedForThisThread) {synchronized (this) {isLockedForThisThread = isLocked || waitingThreads.get(0) != queueObject;if (!isLockedForThisThread) {isLocked = true;waitingThreads.remove(queueObject);lockingThread = Thread.currentThread();return;}}try {queueObject.doWait();} catch (InterruptedException e) {synchronized (this) {waitingThreads.remove(queueObject);}throw e;}}}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) {waitingThreads.get(0).doNotify();}} }public class QueueObject {private boolean isNotified = false;public synchronized void doWait() throws InterruptedException {while (!isNotified) {this.wait();}this.isNotified = false;}public synchronized void doNotify() {this.isNotified = true;this.notify();}public boolean equals(Object o) {return this == o;}}

    首先注意到lock()方法不在聲明為synchronized,取而代之的是對(duì)必需同步的代碼,在synchronized中進(jìn)行嵌套。

    FairLock新創(chuàng)建了一個(gè)QueueObject的實(shí)例,并對(duì)每個(gè)調(diào)用lock()的線程進(jìn)行入隊(duì)列。調(diào)用unlock()的線程將從隊(duì)列頭部獲取QueueObject,并對(duì)其調(diào)用doNotify(),以喚醒在該對(duì)象上等待的線程。通過(guò)這種方式,在同一時(shí)間僅有一個(gè)等待線程獲得喚醒,而不是所有的等待線程。這也是實(shí)現(xiàn)FairLock公平性的核心所在。

    請(qǐng)注意,在同一個(gè)同步塊中,鎖狀態(tài)依然被檢查和設(shè)置,以避免出現(xiàn)滑漏條件。

    還需注意到,QueueObject實(shí)際是一個(gè)semaphore。doWait()和doNotify()方法在QueueObject中保存著信號(hào)。這樣做以避免一個(gè)線程在調(diào)用queueObject.doWait()之前被另一個(gè)調(diào)用unlock()并隨之調(diào)用queueObject.doNotify()的線程重入,從而導(dǎo)致信號(hào)丟失。queueObject.doWait()調(diào)用放置在synchronized(this)塊之外,以避免被monitor嵌套鎖死,所以另外的線程可以解鎖,只要當(dāng)沒(méi)有線程在lock方法的synchronized(this)塊中執(zhí)行即可。

    最后,注意到queueObject.doWait()在try – catch塊中是怎樣調(diào)用的。在InterruptedException拋出的情況下,線程得以離開(kāi)lock(),并需讓它從隊(duì)列中移除。

    性能考慮

    如果比較Lock和FairLock類(lèi),你會(huì)注意到在FairLock類(lèi)中l(wèi)ock()和unlock()還有更多需要深入的地方。這些額外的代碼會(huì)導(dǎo)致FairLock的同步機(jī)制實(shí)現(xiàn)比Lock要稍微慢些。究竟存在多少影響,還依賴(lài)于應(yīng)用在FairLock臨界區(qū)執(zhí)行的時(shí)長(zhǎng)。執(zhí)行時(shí)長(zhǎng)越大,FairLock帶來(lái)的負(fù)擔(dān)影響就越小,當(dāng)然這也和代碼執(zhí)行的頻繁度相關(guān)。

    轉(zhuǎn)載于:https://www.cnblogs.com/EasonJim/p/7026895.html

    總結(jié)

    以上是生活随笔為你收集整理的18、Java并发性和多线程-饥饿与公平的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。