1. 程式人生 > >Java如何保證集合是執行緒安全的?(程式碼實踐拋磚引玉)

Java如何保證集合是執行緒安全的?(程式碼實踐拋磚引玉)

在Java中絕大部分的集合像什麼ArrayList、HashMap等絕大部分不是執行緒安全的。僅有的執行緒安全的實現,像HashTable、Vector等在效能上又不好。但是不要怕啊。我們大Java還有併發包(Java.util.concurrent)啊,為高度併發需求提供了全面安全的支援。

一、在傳統的集合框架中,如何解決執行緒安全問題。

當然,除了Hashtable等同步容器,我們可以使用同步包裝器建立一個執行緒安全的容器。但是這種方式用的是非常粗的同步方式,在高併發情況下,效能比較低下。

具體的位置如下

在這裡插入圖片描述

下面樓主寫了有一些實踐的程式碼:

package com.newframe.controllers.api;

import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.SynchronousQueue;

/**
 * 測試傳統執行緒安全的集合類
 */
public class TestTraditionSyn {

    public static void main(String[] args) {

        /**
         * 1。在傳統的集合框架中。
         * 除了Hashtable這個是執行緒安全的同步容器。
         * 他的實現基本上就是將Put、get、size等各種
         * 方法的操作加上"synchronized"。這就導致了所有的併發操作都在競爭同一把鎖
         * 一個執行緒在進行同步操作時,其他執行緒只能等待,大大降低了併發執行的效率
         * (當然這個因為同步的執行緒開銷較大,不推薦使用)
         * 還可以通過呼叫Collections工具類提供的包裝類。來構造執行緒安全的同步包裝容器,如下所示
         */
        //構造一個執行緒安全的List
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        list.add("hello");
        list.add("world");

        Iterator iterator1 = list.iterator();
        while (iterator1.hasNext()){
            System.out.println(iterator1.next());
        }

        //構造一個執行緒安全的Map
        Map<String,Object> map1 = Collections.synchronizedMap(new HashMap<>());
        map1.put("1","wang");
        map1.put("2","dong");

        map1.forEach((key,value) ->{
            System.out.println("map1:" + key + "," + value);
        });
    }
}

二、重頭戲首選的肯定還是我們的Java併發包啊

具體位置如下:

在這裡插入圖片描述

下面樓主也寫了一些示範如何使用的簡單程式碼:

package com.newframe.controllers.api;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.SynchronousQueue;

/**
 * 測試併發包中的集合類
 */
public class TestConcurrentSyn {

    public static void main(String[] args) {

        /**
         * 2。併發包。在工作中,我們更加普遍的是選擇利用併發包提供
         * 適合在高度併發的環境下使用
         * 執行緒安全容器類
         * 這個只要你是按照併發包的標準建立的集合,都是執行緒安全的。
         */
        //關於map的ConcurrentHashMap
        ConcurrentHashMap<String,Object> map2 = new ConcurrentHashMap<>();
        map2.put("1","我是併發包直接構建的");
        map2.put("2","我是執行緒安全的Map容器,ConcurrentHashMap");
        map2.forEach((key,value) ->{
            System.out.println("map2:" + key + "," + value);
        });

        //關於list的CopyOnWriteArrayList
        CopyOnWriteArrayList<Integer> list2 = new CopyOnWriteArrayList<>();
        list2.add(67612);
        list2.add(67362);
        list2.forEach(list ->{
            System.out.println(list);
        });

        /**
         * 併發包中的執行緒安全佇列
         */
        //ArrayBlockingQueue
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue(10);
        arrayBlockingQueue.add("1");
        arrayBlockingQueue.add("3");
        arrayBlockingQueue.forEach(queue->{
            System.out.println(queue);
        });
    }
}

三、關於Java8以後的ConcurrentHashMap的一點思考。

ConcurrentHashMap的設計實現是一直都在不斷的演化,效能也是在不斷的提高。

早期的ConcurrentHashMap,其實現主要是基於:

  • 分離鎖。在內部進行分段(Segment),裡面則是HashEntry的陣列,和HashMap類似,雜湊相同的條目也是以連結串列的形式存放。
  • HashEntry內部使用volatile的value欄位來保證可見性。

那麼在Java8中,這個有什麼變化呢?

  • 在結構上,雖然仍然有Segment定義,但是僅僅是為了給舊版本相容。初始化已經改成了Lazy-load的形式了,有效避免了初始化開銷。
  • 資料儲存利用的是Volatile來保證可見性。如下圖:

在這裡插入圖片描述

好啦,關於這個裡面的東西太多了,還需要深入研究,當然,在工作中如果能明確應用場景,做出正確的選擇才是關鍵。