目錄
狀態依賴性的管理
首頁 Java java教程 JAVA開發實戰狀態依賴性的管理之阻塞隊列的實現

JAVA開發實戰狀態依賴性的管理之阻塞隊列的實現

Jul 19, 2018 am 11:32 AM

類別庫本身包含了許多存在狀態依賴性的類別。如FutureTask,BlockingQueue等。這些類別中的一些操作,會基於狀態的前提條件。例如,不能從一個空的佇列刪除元素或取得一個尚未結束的任務的計算結果。這兩個操作執行之前,必須等到佇列進入非空狀態或任務進入已完成狀態。我們創建狀態依賴類別最簡單的方法是在類別庫的基礎上進行建構。但如果類別庫沒有你想要的功能,那麼還可以利用Java語言和類別庫提供的底層機制來建構自己的同步機制。

所以,本篇要介紹如何去建構一個自己的狀態依賴類別。從最簡單的構造一步一步介紹到複雜的規範的構造,從而了解這個過程,知道是如何得到最後的結果。

狀態依賴性的管理

可阻塞的狀態依賴操作如下偽代碼所示:

acquire lock on object state //首先获取锁
     while (precondition does not hold) { //前提条件是否满足,不满足则一直循环重试
        release lock //释放锁
        wait until precondition might hold  //等待知道满足前提条件
        optionally fail if interrupted or timeout expire //中断或者超时,各种异常
        reacquire lock //重新获取锁
    }

perform action //执行任务
release lock  //释放锁
登入後複製

取得鎖定,檢查條件是否滿足,如果不滿足,則釋放鎖進入阻塞狀態,直到條件滿足或中斷、逾時等,重新取得鎖。執行任務,釋放鎖。

現在看這個偽代碼可能還不能夠直觀的理解,沒事,往下看,看完這篇文章就知道他的意思了,每個操作都是這個偽代碼架構構造的。

ArrayBlockingQueue是一個有界快取,提供的兩個操作,put 和 take。它們都包含一個前提條件:不能將元素放入到已滿的快取中,不能從空緩存中取得元素。恩,我們的目標就是建構這樣一個ArrayBlockingQueue。

接下來,介紹2種有界快取的實現,它們採用不同的方法來處理前提條件不滿足的情況。

首先,來看下面一個基底類別 BaseBoundeBuffer, 後面的實作都會擴充這個基底類別。它是一個基於數組的循環緩存,包含的變數 buf、head、tail、count都由快取的內建鎖定保護。它還提供了同步的 doPut 和 doTake 方法,並在子類別中,透過這些方法來實作 put 和 take 操作,底層的狀態將對子類別隱藏。

public abstract class BaseBoundedBuffer<V> {

    private final V[] buf;    
    private int tail;    
    private int head;    
    private int count;    
    protected BaseBoundedBuffer(int capacity) {        
    this.buf = (V[]) new Object[capacity];
        count = 0;
    }    
    protected synchronized final void doPut(V v) {
        buf[tail] = v;        if(++tail == buf.length)
            tail = 0;
        ++count;
    }    
    protected synchronized final V doTake() {
        V v = buf[head];
        buf[head] = null;        
        if(++head == buf.length)
        head = 0;
        --count;        
        return v;
    }    
    public synchronized final boolean isFull() {        
        return count == buf.length;
    }    
    public synchronized final boolean isEmpty() {        
        return count == 0;
    }
}
登入後複製

第一種有界快取的實現,對 put 和 take 方法都進行同步,先檢查後執行,失敗則拋出例外。

public class GrumpyBoundedBuffer<V> extends BaseBoundedBuffer{

    protected GrumpyBoundedBuffer(int capacity) {        
        super(capacity);
    }    
    public synchronized void put(V v) throws BufferFullException {        
        if(isFull()) {            
            throw new BufferFullException();
       }
        doPut(v);
    }    
    public synchronized V take() throws BufferFullException {        
        if(isEmpty())            
            throw new BufferFullException();        
            return (V) doTake();
    }
}
登入後複製

如上所示,對於前提條件不滿足的情況,都直接拋出異常,這裡所謂的異常,是指緩存滿或空。實際上來講,這異常不代表程序出錯,打個比方,看到紅燈並不意味著信號燈出現了異常,而是等待直到綠燈在過馬路。所以,這裡的意思是要求在呼叫方捕獲異常,並且每次快取操作時都需要重試。

我們直接來看下面的客戶端呼叫程式碼:

private static GrumpyBoundedBuffer gbb = new GrumpyBoundedBuffer(5);
...while(true) {    try {
        V item = gbb.take();        
        break;
    } catch(BufferEmptyException e) {
        Thread.sleep(500);
    }
}
登入後複製

說白了就是在不滿足前提條件的情況下,再試一次,直到條件滿足,讓看起來能夠達到阻塞的效果。但是這種情況,呼叫者必須自行處理前提條件是失敗的情況,並且一直佔用CPU。這裡的問題是呼叫者使用這個佇列會很麻煩!

第二種方法,SleepyBoundedBuffer 透過輪詢和休眠來實現簡單的阻塞的重試機制,從而使得呼叫者剝離了重試機制,簡化了對快取的使用。請看下面的程式碼清單:

public class SleepyBoundedBuffer<V> extends BaseBoundedBuffer{

    protected SleepyBoundedBuffer(int capacity) {        
        super(capacity);        
        // TODO Auto-generated constructor stub
    }    
    public void put(V v) throws InterruptedException {        
        while(true) {            
            synchronized(this) {                
                if(!isFull()) {
                    doPut(v);                    
                    return;
                }
            }
            Thread.sleep(200);
        }
    }    
    public V take() throws InterruptedException{        
        while(true) {            
            synchronized(this) {                
            if(!isEmpty()) {                    
            return (V) doTake();
                }
            }
            Thread.sleep(200);
        }
    }
}
登入後複製

從呼叫者的角度看,這種方法可以很好的運作。假如某個操作滿足前提條件,則立即執行,否則就阻塞。呼叫者無需處理失敗和重試,但是呼叫者仍然需要處理InterruptedException。與大多數具備良好行為的阻塞庫方法一樣,SleepyBoundedBuffer 透過中斷來支援取消。

SleepyBoundedBuffer的問題在於,睡眠時間設定多長才是合理的?如何才能達到效能的最優?如下圖所示,B執行緒設定條件為真的,但此時A仍在睡眠,這個睡眠就是效能的瓶頸所在了。

恩,有沒有某種方法可以達到,當條件為真時,執行緒立刻醒過來執行呢?
賣個關子,下一篇為你解說!

以上是JAVA開發實戰狀態依賴性的管理之阻塞隊列的實現的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1655
14
CakePHP 教程
1413
52
Laravel 教程
1306
25
PHP教程
1252
29
C# 教程
1226
24
公司安全軟件導致應用無法運行?如何排查和解決? 公司安全軟件導致應用無法運行?如何排查和解決? Apr 19, 2025 pm 04:51 PM

公司安全軟件導致部分應用無法正常運行的排查與解決方法許多公司為了保障內部網絡安全,會部署安全軟件。 ...

如何將姓名轉換為數字以實現排序並保持群組中的一致性? 如何將姓名轉換為數字以實現排序並保持群組中的一致性? Apr 19, 2025 pm 11:30 PM

將姓名轉換為數字以實現排序的解決方案在許多應用場景中,用戶可能需要在群組中進行排序,尤其是在一個用...

如何使用MapStruct簡化系統對接中的字段映射問題? 如何使用MapStruct簡化系統對接中的字段映射問題? Apr 19, 2025 pm 06:21 PM

系統對接中的字段映射處理在進行系統對接時,常常會遇到一個棘手的問題:如何將A系統的接口字段有效地映�...

如何優雅地獲取實體類變量名構建數據庫查詢條件? 如何優雅地獲取實體類變量名構建數據庫查詢條件? Apr 19, 2025 pm 11:42 PM

在使用MyBatis-Plus或其他ORM框架進行數據庫操作時,經常需要根據實體類的屬性名構造查詢條件。如果每次都手動...

IntelliJ IDEA是如何在不輸出日誌的情況下識別Spring Boot項目的端口號的? IntelliJ IDEA是如何在不輸出日誌的情況下識別Spring Boot項目的端口號的? Apr 19, 2025 pm 11:45 PM

在使用IntelliJIDEAUltimate版本啟動Spring...

Java對像如何安全地轉換為數組? Java對像如何安全地轉換為數組? Apr 19, 2025 pm 11:33 PM

Java對象與數組的轉換:深入探討強制類型轉換的風險與正確方法很多Java初學者會遇到將一個對象轉換成數組的�...

電商平台SKU和SPU數據庫設計:如何兼顧用戶自定義屬性和無屬性商品? 電商平台SKU和SPU數據庫設計:如何兼顧用戶自定義屬性和無屬性商品? Apr 19, 2025 pm 11:27 PM

電商平台SKU和SPU表設計詳解本文將探討電商平台中SKU和SPU的數據庫設計問題,特別是如何處理用戶自定義銷售屬...

如何利用Redis緩存方案高效實現產品排行榜列表的需求? 如何利用Redis緩存方案高效實現產品排行榜列表的需求? Apr 19, 2025 pm 11:36 PM

Redis緩存方案如何實現產品排行榜列表的需求?在開發過程中,我們常常需要處理排行榜的需求,例如展示一個�...

See all articles