Java集合中那些類是線程安全的

線程安全,即線程同步。
在多線程併發訪問狀況下,當前某一時刻,僅容許一個線程持有對象的鎖,執行其代碼。執行完畢後,釋放鎖,其餘線程競爭得到該鎖,再進行排他性的訪問java


簡單地說,你看一個類裏面每一個方法都加了synchronized修飾符,那它就是線程安全的。
既然類裏面每個操做都加了線程同步操做,那麼在外面就不用再寫synchronized了。

好比Vector和ArrayList,兩者惟一的區別就是Vector每一個方法都自帶同步機制。
這樣的話,好比我要往集合裏面加一個元素,又要保證多個線程不會同時調用同一個對象的add()方法,ArrayList裏面就要醬紫寫:web

ArrayList<String> list = new ArrayList<>();
synchronized (list) {
    list.add("233");
}

而Vector裏面只要醬紫寫就好了:安全

Vector<String> list = new Vector<>();
list.add("233");

爲何?
由於ArrayList的add方法是醬紫定義的:多線程

public boolean add(E object) {
     ...
}


而Vector的add方法是醬紫定義的:併發

public synchronized boolean add(E object) {
    .....
}

因此一樣調用add()方法,對Vector對象再加synchronized就是畫蛇添足app

線程安全類框架

在集合框架中, 下面是這些線程安全的同步的類:ide

vector:就比arraylist多了個同步化機制(線程安全),由於效率較低,如今已經不太建議使用。在web應用中,特別是前臺頁面,每每效率(頁面響應速度)是優先考慮的。ui

statck:堆棧類,先進後出this

hashtable:就比hashmap多了個線程安全

enumeration:枚舉,至關於迭代器

除了這些以外,其餘的都是非線程安全的類和接口。

線程安全的類其方法是同步的,每次只能一個訪問。是重量級對象,效率較低。

其餘:

1. hashtable跟hashmap的區別

hashtable是線程安全的,即hashtable的方法都提供了同步機制;hashmap不是線程安全的,即不提供同步機制 ;hashtable不容許插入空值,hashmap容許!

不須要線程安全的場景,建議使用HashMap,由於效率高,須要線程安全的場景,建議使用ConcurrentHashMap;

2. 多線程併發修改一 個 集合 怎麼辦

用老的Vector/Hashtable類

StringBuffer是線程安全,而StringBuilder是線程不安全的。對於安全與不安全沒有深刻的理解狀況下,易形成這樣的錯覺,若是對於StringBuffer的操做均是線程安全的,然而,Java給你的保證的線程安全,是說它的方法是執行是排它的,而不是對這個對象自己的屢次調用狀況下,仍是安全的。看看下邊的例子,在StringBufferTest中有一個數據成員contents它是用來擴展的,它的每一次append是線程安全的,但衆屢次append的組合並非線程安全的,這個輸出結果不是太可控的,但若是對於log和getContest方法加關鍵字synchronized,那麼結果就會變得很是條理,若是換成StringBuider甚至是append到一半,它也會讓位於其它在此基礎上操做的線程:

public class StringBufferTest {
   private StringBuffer contents = new StringBuffer();
   public void log(String message){
      contents.append(System.currentTimeMillis());
      contents.append("; ");
      contents.append(Thread.currentThread().getName());
      for(int i=0;i<10000;i++){
        contents.append(i);   
          contents.append(message);    //append自己是線程安全的,修改contents時,其它線程沒法訪問。
          contents.append("\n");
      }
      contents.append("\n\n");
   }
   public void getContents(){
      System.out.println(contents);
   }
}

class RunThread extends Thread{
   String message;
   StringBufferTest buffer;
   public RunThread(StringBufferTest buffer, String message){
      this.buffer = buffer;
      this.message = message;
   }
   public void run(){
      while(true){
         buffer.log(message);
         buffer.getContents();
      }
   }
   public static void main(String[] args) {
      StringBufferTest ss = new StringBufferTest();
      new RunThread(ss, "you").start();
      new RunThread(ss, "me").start();
      new RunThread(ss, "she").start();
   }
}
        StringBuilder和StringBuffer的方法是如出一轍,就是一個多線程和一個單線程的問題。線程調用同一StringBuffer 的append方法,這跟他是否是線程安全沒有關係的,除非你的結果是append的一系列字符串變亂了,那才能說明他是線程不安全的。線程安全是指任什麼時候刻都只有一個線程訪問臨界資源。線程安全 並非說他的一系列操做是同步的 只是對於他執行某個方法的時候不容許別的線程去改變。針對一個類來講是否是線程安全就要看,多個線程在同時在運行,這些線程可能會同時執行某個方法。可是每次運行結果和單線程執行的結果同樣,那麼就能夠說是線程安全的。由於log方法沒有上鎖,每一個如今在append鎖釋放後,均可能獲得cpu的執行片斷。

但不要對多線程安全存在誤解:

   public String toString(){
      StringBuffer buffer = new StringBuffer();
      buffer.append('<');
      buffer.append(this.name);
      buffer.append('>');
      return buffer.toString();
   }

       這個代碼是徹底線程安全的,在方法內部定義的變量,在每一個線程線程進入的時候都會建立這個局部變量!不涉及線程安全問題。一般涉及系統安全的變量通常都是成員變量! stringBuffer自己的內部實現是現場安全的!線程安全那是類自己提供的功能是安全的。即你提供插入一個字符串,那麼這個字符串插入是安全的,可是要插入兩個字符串,兩個的順序你來定,這之間若是有別的插入出錯就無論類的事情了,是你本身代碼的問題。

相關文章
相關標籤/搜索