Java多線程設計模式(2)

來源:互聯網
上載者:User

《Java多線程設計模式》讀書筆記2

目錄:
1 Java的記憶體模型
2 Single Threaded Execution Pattern
3 Guarded Suspension Pattern
4 Balking Pattern
5 Producer-Consumer Pattern
6 Read-Write Lock Pattern

=============Java的記憶體模型======================

Java的記憶體模型分為主儲存空間與工作儲存空間兩種。
主儲存空間(與硬體上的主存無關,僅為抽象概念)就是執行個體位置所在的地區,所有的執行個體都存在於主儲存空間內。尤其是執行個體所擁有的欄位即位於主儲存空間內的地區。主儲存空間為所有線程所共有。工作儲存空間為各個線程所擁有的作業區,所有的線程都有其專用的工作儲存空間。在工作儲存空間中存有主儲存空間中必要的拷貝,稱之為工作拷貝。
線程無法直接對主儲存空間直接進行操作,因此也無法直接引用欄位的值,當線程需要引用欄位的值時,需要把它從主儲存空間上拷貝到自己的工作儲存空間中。至於要修改欄位的值時,也要先修改工作拷貝,然後把修改從工作儲存空間拷貝回主儲存空間中。至於何時完成資料在工作儲存空間和主儲存空間之間的映像,則有Java執行處理系統決定。
對於多個線程共用的欄位,一般由synchronized或者volatile來保護。
synchronized用來完成線程的共用互斥。而volatile不負責線程的共用互斥,僅負責記憶體的同步。
synchronized一方面製作臨界區,使得該地區只能同時允許一個線程進行操作;另一方面,當進入和退出臨界區時,執行記憶體的同步操作,即把當前工作區的修改映像到主儲存空間上。在進入synchronized時,除了工作拷貝到主儲存空間的映像之外,還會釋放工作儲存空間的工作拷貝,如果之後再引用主儲存空間上值,則必須從主儲存空間拷貝到工作儲存空間。
雖然在進入和離開synchronized時,會發生記憶體的同步,但是如果是“在synchronized內”或者“在synchronized外”時,記憶體何時同步是不確定的,取決於Java運行處理系統。
在單線程中,可以不考慮記憶體的同步問題,當然線程的同步也不需考慮。
volatile記憶體同步:當線程欲引用volatile欄位的值時,通常都會發生從主儲存空間到工作儲存空間的拷貝操作,反之,將值指定給寫著volatile的欄位後,工作儲存空間的內容通常會映像到主儲存空間。另外,對於long和double類型,預設的賦值操作不是atomic的,如要以atomic方式指定這些類型的值,則需使用volatile關鍵字,此時,volatile實現原子操作,而非記憶體同步。問:如果要指定long,double類型欄位的記憶體同步,怎麼辦?難道只能用synchronized來實現?

=============Single Threaded Execution Pattern=============

當我們修改由多個線程共用的變數,變數就會失去安全性。所以我們應該仔細找出變數狀態不安定的範圍,將這個範圍設定為臨界區,並對臨界區施加保護,限制同時只能有一個線程執行它。在java中,可以使用synchronized關鍵字定義臨界區,保護共用欄位。
適用性:資料可被多個線程訪問,並且狀態可能變化的時候。
===================Immutable Pattern===============
當一個類的執行個體聲明之後,狀態就完全不再改變,此時就算用多個線程同時調用這個類的方法也無所謂,所以方法沒有synchronized的必要,這樣一來,可以在不喪失安全性與生命性的前提下,提高程式的效能。而要實作類別的immutable,則在類的實現中必須小心防備,不要提供能夠改變執行個體狀態的方法或者破綻。

Java final的用法
final類:final類無法被繼承
final方法:如果是執行個體方法,則無法被子類所覆蓋,若是類方法,則無法被子類隱藏。
final欄位:其值只能指定一次,要麼在聲明時初始化,要麼在建構函式中賦值(靜態欄位則是在static塊中初始化)。
final變數與參數:final變數只能在定義時指定一次,final參數則是僅在調用時傳值指定。

===================Guarded Suspension Pattern===============

當滿足某條件時,直接執行某動作;如果不滿足條件,等待,直到條件滿足時被喚醒。
比如請求隊列,如果隊列已經為空白,則此時要執行擷取請求操作則必須等待;當添加新請求時喚醒等待的線程。
public synchronized Request getRequest(){
    while(queue.size() <= 0){
        try{
        wait();
    }catch(InterruptedException e){}
    return (Request)queue.removeFirst();
}
public synchronized void putRequest(Request req){
    queue.addLast(req);
    notifyAll();
}

===================Balking Pattern===============

當現在不適合進行某個操作,或者沒有必要進行某個操作時,就直接放棄進行這個操作而返回。
public synchronized void save(){
    if(!changed){
      return;
    }
    dosave();
    changed = false;
}
使用情境:(1)不需要刻意去執行的時候(提高執行效能);(2)不想等待警戒條件時(提高響應性);(3)警戒條件只有第一次成立時(比如初始化)。

===================Producer-Consumer Pattern===============

Producer-Consumer模式假設所生產的產品放置在一個長度有限制的緩衝區,如果緩衝區滿了,則生產者必須停止繼續將產品放到緩衝區中,直到消費者取走了產品而有了空間,而如果緩衝區中沒有產品,當然消費者必須等待,直到有新的產品放到緩衝區中。與Guarded Suspension 模式類似的,只不過Guarded Suspension模式並不限制緩衝區的長度。
可能有多個生產者和多個消費者,當只有一個生產者和一個消費者的時候,又叫管道Pipe Pattern。
Java主要通過synchronized,wait與notify/notifyAll方法來實現producer與consumer的協調。

===================Read-Write Lock Pattern===============

Read-Write Lock Pattern將讀取與寫入分開處理。在讀取資料之前,必須擷取用來讀取的鎖定。而要寫入的時候,則必須擷取用來寫入的鎖定。
因為讀取的時候,執行個體的狀態不會改變,所以就算有多個線程在同時讀取也沒有關係。但有人在讀取的時候就不可做寫入的操作。
寫入的時候,執行個體的狀態就會改變,當有一個線程在寫入的時候,其他的線程不可以進行讀取或寫入。一般來說,進行共用互斥會使程式效能變差,但將寫入的共用互斥與讀取的共用互斥分開思考,就可以提升程式的效能。
Java語言中,使用finally可以避免忘記解除鎖定。比如:
擷取鎖定
try{
    實際的操作   
}finally{
    解除鎖定
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.