1. 程式人生 > 其它 >Java併發:五種執行緒安全型別、執行緒安全的實現、列舉型別

Java併發:五種執行緒安全型別、執行緒安全的實現、列舉型別

1. Java中的執行緒安全

  • Java執行緒安全:狹義地認為是多執行緒之間共享資料的訪問。
  • Java語言中各種操作共享的資料有5種類型:不可變、絕對執行緒安全、相對執行緒安全、執行緒相容、執行緒獨立

① 不可變

  • 不可變(Immutable) 的物件一定是執行緒安全的,不需要再採取任何的執行緒安全保障措施。
  • 只要能正確構建一個不可變物件,該物件永遠不會在多個執行緒之間出現不一致的狀態。
  • 多執行緒環境下,應當儘量使物件成為不可變,來滿足執行緒安全。

實現不可變=========》

  • 如果共享資料是基本資料型別,使用final關鍵字對其進行修飾,就可以保證它是不可變的。
  • 如果共享資料是一個物件,要保證物件的行為不會對其狀態產生任何影響。
  • String是不可變的,對其進行substring()、replace()、concat()等操作,返回的是新的String物件,原始的String物件的值不受影響。而如果對StringBuffer或者StringBuilder物件進行substring()、replace()、append()等操作,直接對原物件的值進行改變。
  • 要構建不可變物件,需要將內部狀態變數定義為final型別。如java.lang.Integer類中將value定義為final型別。=====》privatefinalintvalue;

常見的不可變的型別:

  • final關鍵字修飾的基本資料型別
  • 列舉型別、String型別
  • 常見的包裝型別:Short、Integer、Long、Float、Double、Byte、Character等
  • 大資料型別:BigInteger、BigDecimal

對於集合型別,可以使用Collections.unmodifiableXXX()方法來獲取一個不可變的集合。

  • 通過Collections.unmodifiableMap(map)獲的一個不可變的Map型別。
  • Collections.unmodifiableXXX()先對原始的集合進行拷貝,需要對集合進行修改的方法都直接丟擲異常。

例如,如果獲得的不可變map物件進行put()、remove()、clear()操作,則會丟擲UnsupportedOperationException異常

② 絕對執行緒安全

絕對執行緒安全的實現,通常需要付出很大的、甚至不切實際的代價。

Java API中提供的執行緒安全,大多數都不是絕對執行緒安全。

例如,對於陣列集合Vector的操作,如get()、add()、remove()都是有synchronized關鍵字修飾。有時呼叫時也需要手動新增同步手段,保證多執行緒的安全。

下面的程式碼看似不需要同步,實際執行過程中會報錯。

importjava.util.Vector;

/**
*@Author:lucy
*@Version1.0
*/
publicclassVectorTest{
publicstaticvoidmain(String[]args){
Vector<Integer>vector=newVector<>();
while(true){
for(inti=0;i<10;i++){
vector.add(i);
}
newThread(newRunnable(){
@Override
publicvoidrun(){
for(inti=0;i<vector.size();i++){
System.out.println("獲取vector的第"+i+"個元素:"+vector.get(i));
}
}
}).start();
newThread(newRunnable(){
@Override
publicvoidrun(){
for(inti=0;i<vector.size();i++){
System.out.println("刪除vector中的第"+i+"個元素");
vector.remove(i);
}
}
}).start();
while(Thread.activeCount()>20)
return;
}
}
}

出現ArrayIndexOutOfBoundsException異常,原因:某個執行緒恰好刪除了元素i,使得當前執行緒無法訪問元素i。

Exceptioninthread"Thread-1109"java.lang.ArrayIndexOutOfBoundsException:Arrayindexoutofrange:1
atjava.util.Vector.remove(Vector.java:831)
atVectorTest$2.run(VectorTest.java:28)
atjava.lang.Thread.run(Thread.java:745)

需要將對元素的get和remove構造成同步程式碼塊:

synchronized(vector){
for(inti=0;i<vector.size();i++){
System.out.println("獲取vector的第"+i+"個元素:"+vector.get(i));
}
}
synchronized(vector){
for(inti=0;i<vector.size();i++){
System.out.println("刪除vector中的第"+i+"個元素");
vector.remove(i);
}
}


③ 相對執行緒安全

  • 相對執行緒安全需要保證對該物件的單個操作是執行緒安全的,在必要的時候可以使用同步措施實現執行緒安全。
  • 大部分的執行緒安全類都屬於相對執行緒安全,如Java容器中的Vector、HashTable、通過Collections.synchronizedXXX()方法包裝的集合。

④ 執行緒相容

  • Java中大部分的類都是執行緒相容的,通過新增同步措施,可以保證在多執行緒環境中安全使用這些類的物件。
  • 如常見的ArrayList、HashTableMap都是執行緒相容的。

⑤ 執行緒對立

  • 執行緒對立是指:無法通過新增同步措施,實現多執行緒中的安全使用。
  • 執行緒對立的常見操作有:Thread類的suspend()和resume()(已經被JDK宣告廢除),System.setIn()System.setOut()等。

2. Java的列舉型別

通過enum關鍵字修飾的資料型別,叫列舉型別。

  • 列舉型別的每個元素都有自己的序號,通常從0開始編號。
  • 可以通過values()方法遍歷列舉型別,通過name()或者toString()獲取列舉型別的名稱
  • 通過ordinal()方法獲取列舉型別中元素的序號

publicclassEnumData{
publicstaticvoidmain(String[]args){
for(Familyfamily:Family.values()){
System.out.println(family.name()+":"+family.ordinal());
}
}
}

enumFamily{
GRADMOTHER,GRANDFATHER,MOTHER,FATHER,DAUGHTER,SON;
}

3. Java執行緒安全的實現

① 互斥同步

互斥同步(Mutex Exclusion & Synchronization)是一種常見的併發正確性保障手段。

  • 同步:多個執行緒併發訪問共享資料,保證共享資料同一時刻只被一個(或者一些,使用訊號量)執行緒使用。
  • 互斥:互斥是實現同步的一種手段,主要的互斥實現方式:臨界區(Critical Section)、互斥量(Mutex)、訊號量(Semaphore)。

同步與互斥的關係:

  • 互斥是原因,同步是結果。
  • 同步是目的,互斥是方法。

Java中,最基本的實現互斥同步的手段是synchronized關鍵字,其次是JUC包中的ReentrantLock。