一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - JAVA教程 - Java多線程饑餓與公平介紹及代碼示例

Java多線程饑餓與公平介紹及代碼示例

2021-01-30 11:57mengwei JAVA教程

這篇文章主要介紹了Java多線程饑餓與公平介紹及代碼示例,分析饑餓產生的原因以及相關實例,然后就在java中實現公平性問題做了詳細解析,具有一定參考價值,需要的朋友可以了解下。

如果一個線程因為CPU時間全部被其他線程搶走而得不到CPU運行時間,這種狀態被稱之為“饑餓”。而該線程被“饑餓致死”正是因為它得不到CPU運行時間的機會。解決饑餓的方案被稱之為“公平性” – 即所有線程均能公平地獲得運行機會。

 下面是本文討論的主題:

Java中導致饑餓的原因

在Java中,下面三個常見的原因會導致線程饑餓:

高優先級線程吞噬所有的低優先級線程的CPU時間。
線程被永久堵塞在一個等待進入同步塊的狀態,因為其他線程總是能在它之前持續地對該同步塊進行訪問。
線程在等待一個本身(在其上調用wait())也處于永久等待完成的對象,因為其他線程總是被持續地獲得喚醒。

高優先級線程吞噬所有的低優先級線程的CPU時間

你能為每個線程設置獨自的線程優先級,優先級越高的線程獲得的CPU時間越多,線程優先級值設置在1到10之間,而這些優先級值所表示行為的準確解釋則依賴于你的應用運行平臺。對大多數應用來說,你最好是不要改變其優先級值。

線程被永久堵塞在一個等待進入同步塊的狀態

Java的同步代碼區也是一個導致饑餓的因素。Java的同步代碼區對哪個線程允許進入的次序沒有任何保障。這就意味著理論上存在一個試圖進入該同步區的線程處于被永久堵塞的風險,因為其他線程總是能持續地先于它獲得訪問,這即是“饑餓”問題,而一個線程被“饑餓致死”正是因為它得不到CPU運行時間的機會。

線程在等待一個本身(在其上調用wait())也處于永久等待完成的對象

如果多個線程處在wait()方法執行上,而對其調用notify()不會保證哪一個線程會獲得喚醒,任何線程都有可能處于繼續等待的狀態。因此存在這樣一個風險:一個等待線程從來得不到喚醒,因為其他等待線程總是能被獲得喚醒。

下面分享一個線程饑餓死鎖的例子,代碼:

java" id="highlighter_809808">
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class ThreadDeadlock {
    ExecutorService exec = Executors.newSingleThreadScheduledExecutor();
    //  ExecutorService exec = Executors.newCachedThreadPool();  //如果添加給線程池中添加足夠多的線程,就可以讓所有任務都執行,避免饑餓死鎖。
    /**
   * 模擬頁面加載的例子
   *
   * 產生死鎖分析:
   * RenderPageTask任務中有2個子任務分別是“加載頁眉”和“加載頁腳”。當提交RenderPageTask任務時,實際上是向線程池中添加了3個任務,
   * 但是由于線程池是單一線程池,同時只會執行一個任務,2個子任務就會在阻塞在線程池中。而RenderPageTask任務由于得不到返回,也會
   * 一直堵塞,不會釋放線程資源讓子線程執行。這樣就導致了線程饑餓死鎖。
   *
   * 在一個Callable任務中,要返回2個子任務
   * @author hadoop
   *
   */
    class RenderPageTask implements Callable<String>{
        @Override
            public String call() throws Exception {
            Future<String> header,footer;
            header = exec.submit(new Callable<String>(){
                @Override
                        public String call() throws Exception {
                    System.out.println("加載頁眉");
                    Thread.sleep(2*1000);
                    return "頁眉";
                }
            }
            );
            footer = exec.submit(new Callable<String>(){
                @Override
                        public String call() throws Exception {
                    System.out.println("加載頁腳");
                    Thread.sleep(3*1000);
                    return "頁腳";
                }
            }
            );
            System.out.println("渲染頁面主體");
            return header.get() + footer.get();
        }
    }
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ThreadDeadlock td = new ThreadDeadlock();
        Future<String> futre = td.exec.submit(td.new RenderPageTask());
        String result = futre.get();
        System.out.println("執行結果為:" + result);
    }
}

在Java中實現公平性

雖Java不可能實現100%的公平性,我們依然可以通過同步結構在線程間實現公平性的提高。

首先來學習一段簡單的同步態代碼:

?
1
2
3
4
5
public class Synchronizer{
  public synchronized void doSynchronized(){
  //do a lot of work which takes a long time
  }
}

如果有一個以上的線程調用doSynchronized()方法,在第一個獲得訪問的線程未完成前,其他線程將一直處于阻塞狀態,而且在這種多線程被阻塞的場景下,接下來將是哪個線程獲得訪問是沒有保障的。

使用鎖方式替代同步塊

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

?
1
2
3
4
5
6
7
8
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 time
    this.lock.unlock();
  }
}

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

下面是用Lock類做的一個實現:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();
    }
}

注意到上面對Lock的實現,如果存在多線程并發訪問lock(),這些線程將阻塞在對lock()方法的訪問上。另外,如果鎖已經鎖上(校對注:這里指的是isLocked等于true時),這些線程將阻塞在while(isLocked)循環的wait()調用里面。要記住的是,當線程正在等待進入lock() 時,可以調用wait()釋放其鎖實例對應的同步鎖,使得其他多個線程可以進入lock()方法,并調用wait()方法。

這回看下doSynchronized(),你會注意到在lock()和unlock()之間的注釋:在這兩個調用之間的代碼將運行很長一段時間。進一步設想,這段代碼將長時間運行,和進入lock()并調用wait()來比較的話。這意味著大部分時間用在等待進入鎖和進入臨界區的過程是用在wait()的等待中,而不是被阻塞在試圖進入lock()方法中。

在早些時候提到過,同步塊不會對等待進入的多個線程誰能獲得訪問做任何保障,同樣當調用notify()時,wait()也不會做保障一定能喚醒線程(至于為什么,請看線程通信)。因此這個版本的Lock類和doSynchronized()那個版本就保障公平性而言,沒有任何區別。

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

公平鎖

下面來講述將上面Lock類轉變為公平鎖FairLock。你會注意到新的實現和之前的Lock類中的同步和wait()/notify()稍有不同。
準確地說如何從之前的Lock類做到公平鎖的設計是一個漸進設計的過程,每一步都是在解決上一步的問題而前進的:Nested Monitor Lockout, Slipped Conditions和Missed Signals。這些本身的討論雖已超出本文的范圍,但其中每一步的內容都將會專題進行討論。重要的是,每一個調用lock()的線程都會進入一個隊列,當解鎖后,只有隊列里的第一個線程被允許鎖住Farlock實例,所有其它的線程都將處于等待狀態,直到他們處于隊列頭部。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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();
        }
    }
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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,取而代之的是對必需同步的代碼,在synchronized中進行嵌套。

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

請注意,在同一個同步塊中,鎖狀態依然被檢查和設置,以避免出現滑漏條件。

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

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

性能考慮

如果比較Lock和FairLock類,你會注意到在FairLock類中lock()和unlock()還有更多需要深入的地方。這些額外的代碼會導致FairLock的同步機制實現比Lock要稍微慢些。究竟存在多少影響,還依賴于應用在FairLock臨界區執行的時長。執行時長越大,FairLock帶來的負擔影響就越小,當然這也和代碼執行的頻繁度相關。

總結

以上就是本文關于Java多線程饑餓與公平介紹及代碼示例的全部內容,希望對大家有所幫助。有什么問題可以隨時留言,小編會及時回復大家的。感謝朋友們對本站的支持!

原文鏈接:http://ifeve.com/starvation-and-fairness/

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲福利一区二区 | 香蕉精品高清在线观看视频 | 日韩天堂在线 | 欧美性色老妇人 | 国产色拍 | 国产精品嫩草影院在线看 | 日本高清视频网站www | 亚洲色图第四页 | 校园情射 | 丝瓜香蕉视频 | 日韩大片免费观看 | 成人小视频在线观看 | 亚洲精品国产国语 | 九九精品免视看国产成人 | 校花的第一次好紧好爽 | 情侣奴伺候女王第2部分小说 | a毛片免费观看完整 | 奇米影视久久777中文字幕 | 青青青视频免费观看 | 亚洲国产成人久久精品影视 | 国产在线观看网站 | 国产精品一区二区不卡的视频 | 按摩师他揉我奶好爽捏我奶 | 亚洲国产精品久久久久 | 国产精品怡红院永久免费 | 亚洲视频一区在线播放 | 久久99re2在线视频精品 | 午夜福利院电影 | 边吃胸边膜下刺激免费男对女 | 美女扒开屁股让我桶免费 | 极品美女写真菠萝蜜视频 | 插鸡视频在线观看 | 夫妻性生活影院 | 国产高清露脸学生在线观看 | 青青草国产精品 | 国产成人精品一区二区不卡 | 好湿好紧太硬了我好爽 | 国内精品久久久久影院网站 | 久久久这里有精品999 | julia ann多人乱战 | 大胸孕妇孕交pregnantsex 大象视频污 |