《java並發編程實戰》筆記(一)

來源:互聯網
上載者:User

標籤:new   java   實體   ==   預測   alt   同步代碼塊   jvm   修改   

  最近在看《java並發編程實戰》,希望自己有毅力把它讀完。

  線程本身有很多優勢,比如可以發揮多處理器的強大能力、建模更加簡單、簡化非同步事件的處理、使使用者介面的相應更加靈敏,但是更多的需要程式猿面對的是安全性問題。看下面例子:

public class UnsafeSequence {    private int value;        /*返回一個唯一的數值*/    public int getNext(){        return value++;    }}

  UnsafeSequence的問題在於,如果執行時機不對,那麼兩個線程在調用getNext時會得到相同的值,圖1給出了這種錯誤情況。雖然遞增運算value++看上去是單個操作,但事實上它包含三個獨立的操作:  讀取value、將value加1、將計算結果寫入value。由於運行時可能將多個線程之間的操作交替執行,因此這兩個線程可能同時執行讀操作,從而使它們得到相同的值,並都將這個值加1。結果就是,在不同線程的調用中返回了相同的值。

 

在UnsafeSequence中說明的是一種常見的並發安全問題,稱為競態條件。當某個計算的正確性取決於多個線程的交替執行時序時,那麼就會發生競態條件。

 

再舉個例子,延遲初始化中的競態條件:

public class LazyInitRace {    private HashMap<String, String> instance = null;        public HashMap<String, String> getInstance(){        if (instance == null) {            instance = new HashMap<String, String>();        }                return instance;    }}

  在LazyInitRace中包含一個競態條件,它可能會破壞這個類的正確性。假定線程A和線程B同時執行getInstance,A看到instance為空白,因而建立一個新的HashMap實體,B同樣需要判斷instance是否為空白,此時的instance是否為空白,要取決於不可預測的時序,如果當B檢查時,instance也為空白,那麼在兩次調用getInstance時可能會得到不同的結果,即使getInstance通常被認為是返回相同的執行個體。

  java提供了鎖機制來解決這一問題,但這些終歸只是一些機制,要編寫安全執行緒的代碼,其核心在於要對狀態訪問操作進行管理。

一、對象的狀態是指儲存在狀態變數(例如執行個體或者靜態域)中的資料。如果一個對象無狀態,它一定是安全執行緒的。

  

public class StatelessServlet implements Servlet {    public void service(ServletRequest request, ServletResponse response)            throws ServletException, IOException {        int i = 1;        i++;        ...    }}

與大多數servlet相同,StatelessServlet是無狀態的:它即不包含任何域,也不包含任何對其他類中域的引用。計算過程中的臨時狀態僅存在於線程棧上的局部變數中(這塊需要對jvm記憶體配置有基礎瞭解),並且只能由正在執行的線程訪問。線程之間沒有共用狀態,由於線程訪問無狀態對象的行為並不會影響其他線程中操作的正確性,因此無狀態對象是安全執行緒的。

二、用鎖來保護狀態

  內建鎖

  java提供了一種內建的鎖機制來支援原子性:同步代碼塊。同步代碼塊包括兩部分:一個作為鎖的對象引用,一個作為由這個鎖保護的代碼塊。以關鍵字synchronized來修飾的方法就是一種橫跨整個方法體的同步代碼塊,其中該同步代碼塊的鎖就是方法調用所在的對象,一般不要這麼做,這樣會影響效率。

  synchronized(lock){

  //訪問或修改由鎖保護的共用狀態

  }

  每一個java對象都可以用作一個實現同步的鎖,這些鎖被稱為內建鎖或者監視器鎖。線程在進入同步代碼塊之前會自動獲得鎖,並且在退出同步代碼塊時自動釋放鎖。

  對象的內建鎖與其狀態之間沒有內在的關聯。雖然大多數類都將內建鎖用做一種有效加鎖機制,但對象的域不一定要通過內建鎖來保護。當擷取與對象關聯的鎖時,並不能阻止其他線程訪問該對象,某個線程在獲得對象的鎖以後,只能阻止其他線程獲得同一個鎖。之所以每個對象都有一個內建鎖,只是為了免去顯式的建立鎖對象。

  開發中常見的內建鎖的使用方法是,將所有的可變狀態都封裝在對象內部,並通過對象的內建鎖對所有訪問可變狀態餓代碼路徑進行同步,使得在該對象上不會發生並發訪問,例如,Vector和其他的同步集合類。

  

總之,以對象的狀態來理解安全執行緒以及為什麼加鎖,是這部分的核心意思。

《java並發編程實戰》筆記(一)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.