qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

          Java并發編程:同步容器

          為了方便編寫出線程安全的程序,Java里面提供了一些線程安全類和并發工具,比如:同步容器、并發容器、阻塞隊列、Synchronizer(比如CountDownLatch)。今天我們就來討論下同步容器。
            一.為什么會出現同步容器?
            在Java的集合容器框架中,主要有四大類別:List、Set、Queue、Map。
            List、Set、Queue接口分別繼承了Collection接口,Map本身是一個接口。
            注意Collection和Map是一個頂層接口,而List、Set、Queue則繼承了Collection接口,分別代表數組、集合和隊列這三大類容器。
            像ArrayList、LinkedList都是實現了List接口,HashSet實現了Set接口,而Deque(雙向隊列,允許在隊首、隊尾進行入隊和出隊操作)繼承了Queue接口,PriorityQueue實現了Queue接口。另外LinkedList(實際上是雙向鏈表)實現了了Deque接口。
            像ArrayList、LinkedList、HashMap這些容器都是非線程安全的。
            如果有多個線程并發地訪問這些容器時,就會出現問題。
            因此,在編寫程序時,必須要求程序員手動地在任何訪問到這些容器的地方進行同步處理,這樣導致在使用這些容器的時候非常地不方便。
            所以,Java提供了同步容器供用戶使用。
            二.Java中的同步容器類
            在Java中,同步容器主要包括2類:
            1)Vector、Stack、HashTable
            2)Collections類中提供的靜態工廠方法創建的類
            Vector實現了List接口,Vector實際上就是一個數組,和ArrayList類似,但是Vector中的方法都是synchronized方法,即進行了同步措施。
            Stack也是一個同步容器,它的方法也用synchronized進行了同步,它實際上是繼承于Vector類。
            HashTable實現了Map接口,它和HashMap很相似,但是HashTable進行了同步處理,而HashMap沒有。
            Collections類是一個工具提供類,注意,它和Collection不同,Collection是一個頂層的接口。在Collections類中提供了大量的方法,比如對集合或者容器進行排序、查找等操作。最重要的是,在它里面提供了幾個靜態工廠方法來創建同步容器類,如下圖所示:
            三.同步容器的缺陷
            從同步容器的具體實現源碼可知,同步容器中的方法采用了synchronized進行了同步,那么很顯然,這必然會影響到執行性能,另外,同步容器就一定是真正地完全線程安全嗎?不一定,這個在下面會講到。
            我們首先來看一下傳統的非同步容器和同步容器的性能差異,我們以ArrayList和Vector為例:
            1.性能問題
            我們先通過一個例子看一下Vector和ArrayList在插入數據時性能上的差異:
          public class Test {
          public static void main(String[] args) throws InterruptedException {
          ArrayList<Integer> list = new ArrayList<Integer>();
          Vector<Integer> vector = new Vector<Integer>();
          long start = System.currentTimeMillis();
          for(int i=0;i<100000;i++)
          list.add(i);
          long end = System.currentTimeMillis();
          System.out.println("ArrayList進行100000次插入操作耗時:"+(end-start)+"ms");
          start = System.currentTimeMillis();
          for(int i=0;i<100000;i++)
          vector.add(i);
          end = System.currentTimeMillis();
          System.out.println("Vector進行100000次插入操作耗時:"+(end-start)+"ms");
          }
          }
          這段代碼在我機器上跑出來的結果是:
            進行同樣多的插入操作,Vector的耗時是ArrayList的兩倍。
            這只是其中的一方面性能問題上的反映。
            另外,由于Vector中的add方法和get方法都進行了同步,因此,在有多個線程進行訪問時,如果多個線程都只是進行讀取操作,那么每個時刻就只能有一個線程進行讀取,其他線程便只能等待,這些線程必須競爭同一把鎖。
            因此為了解決同步容器的性能問題,在Java 1.5中提供了并發容器,位于java.util.concurrent目錄下,并發容器的相關知識將在下一篇文章中講述。
            2.同步容器真的是安全的嗎?
            也有有人認為Vector中的方法都進行了同步處理,那么一定就是線程安全的,事實上這可不一定。看下面這段代碼:
          public class Test {
          static Vector<Integer> vector = new Vector<Integer>();
          public static void main(String[] args) throws InterruptedException {
          while(true) {
          for(int i=0;i<10;i++)
          vector.add(i);
          Thread thread1 = new Thread(){
          public void run() {
          for(int i=0;i<vector.size();i++)
          vector.remove(i);
          };
          };
          Thread thread2 = new Thread(){
          public void run() {
          for(int i=0;i<vector.size();i++)
          vector.get(i);
          };
          };
          thread1.start();
          thread2.start();
          while(Thread.activeCount()>10)   {
          }
          }
          }
          }
            在我機器上運行的結果:
            正如大家所看到的,這段代碼報錯了:數組下標越界。
            也許有朋友會問:Vector是線程安全的,為什么還會報這個錯?很簡單,對于Vector,雖然能保證每一個時刻只能有一個線程訪問它,但是不排除這種可能:
            當某個線程在某個時刻執行這句時:
            for(int i=0;i<vector.size();i++)
            vector.get(i);
            假若此時vector的size方法返回的是10,i的值為9
            然后另外一個線程執行了這句:
            for(int i=0;i<vector.size();i++)
            vector.remove(i);
            將下標為9的元素刪除了。
            那么通過get方法訪問下標為9的元素肯定就會出問題了。
            因此為了保證線程安全,必須在方法調用端做額外的同步措施,如下面所示:
          public class Test {
          static Vector<Integer> vector = new Vector<Integer>();
          public static void main(String[] args) throws InterruptedException {
          while(true) {
          for(int i=0;i<10;i++)
          vector.add(i);
          Thread thread1 = new Thread(){
          public void run() {
          synchronized (Test.class) {   //進行額外的同步
          for(int i=0;i<vector.size();i++)
          vector.remove(i);
          }
          };
          };
          Thread thread2 = new Thread(){
          public void run() {
          synchronized (Test.class) {
          for(int i=0;i<vector.size();i++)
          vector.get(i);
          }
          };
          };
          thread1.start();
          thread2.start();
          while(Thread.activeCount()>10)   {
          }
          }
          }
          }
            3. ConcurrentModificationException異常
            在對Vector等容器并發地進行迭代修改時,會報ConcurrentModificationException異常,關于這個異常將會在后續文章中講述。
            但是在并發容器中不會出現這個問題。

          posted on 2014-10-16 09:58 順其自然EVO 閱讀(202) 評論(0)  編輯  收藏 所屬分類: 測試學習專欄

          <2014年10月>
          2829301234
          567891011
          12131415161718
          19202122232425
          2627282930311
          2345678

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 时尚| 林甸县| 都兰县| 黔江区| 青川县| 河北区| 梅河口市| 酒泉市| 华安县| 南木林县| 乌拉特后旗| 安陆市| 宝兴县| 北流市| 洪雅县| 广东省| 射阳县| 保德县| 留坝县| 琼结县| 东阿县| 锡林浩特市| 饶河县| 甘谷县| 蓝山县| 大同县| 炉霍县| 哈尔滨市| 水富县| 泰州市| 百色市| 顺义区| 确山县| 班玛县| 金平| 黎城县| 永和县| 宿州市| 无锡市| 巴彦县| 扎鲁特旗|