Java裏的併發容器與安全共享策略總結

1、併發容器

ArrayList --> CopyOnWriteArrayList

概念 : 簡單的講就是寫操做時賦值,當有新元素添加到CopyOnWriteArrayList時,它先從原有的數組裏邊Copy一份出來而後在新的數組上作些操做,操做完成之後在將引用指向新的數組;CopyOnWriteArrayList全部的操做都是在鎖的保護下進行的,這樣作的目的主要是爲了在多線程併發作add操做的時候複製出多個副本出來致使數據混亂;java

缺點 :

① 因爲是copy的操做因此比較消耗內存,若是元素的內容較多的時候可能會觸發GC,數組

② 不能用於實時讀的場景,它比較適合讀多寫少的場景;安全

思想 :

① 讀寫分離;多線程

② 最終一致性;併發

③ 另外開闢空間解決併發衝突;優化

// CopyOnWriteArrayList
@Slf4j
@ThreadSafe
public class CopyOnWriteArrayListExample {
 
    // 請求總數
    public static int clientTotal = 5000;
 
    // 同時併發執行的線程數
    public static int threadTotal = 200;
 
    private static List<Integer> list = new CopyOnWriteArrayList<>();
 
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal; i++) {
            final int count = i;
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    update(count);
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("size:{}", list.size());
    }
 
    private static void update(int i) {
        list.add(i);
    }
}


HashSet --> CopyOnWriteArraySet 與 TreeSet --> ConcurrentSkipListSet

概念 :ui

CopyOnWriteArraySet它是線程安全,底層實現是使用CopyOnWriteArrayList,它的不少特性都與CopyOnWriteArrayList類似包括適用場景;spa

ConcurrentSkipListSet是jdk6新增的類,支持天然排序,能夠在構造的時候本身定義比較器,它是基於Map集合的,在多線程環境下ConcurrentSkipListSet它裏邊的remote add 等方法都是線程安全的,可是對於批量操做並不能保證以原子方式進行操做,在批量操做的時候只能保證每一次的操做是原子性的;ConcurrentSkipListSet在使用批量操做的時候可能須要手動處理一下;線程

// CopyOnWriteArraySet
@Slf4j
@ThreadSafe
public class CopyOnWriteArraySetExample {
 
    // 請求總數
    public static int clientTotal = 5000;
 
    // 同時併發執行的線程數
    public static int threadTotal = 200;
 
    private static Set<Integer> set = new CopyOnWriteArraySet<>();
 
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal; i++) {
            final int count = i;
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    update(count);
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("size:{}", set.size());
    }
 
    private static void update(int i) {
        set.add(i);
    }
}

// ConcurrentSkipListSet
@Slf4j
@ThreadSafe
public class ConcurrentSkipListSetExample {
 
    // 請求總數
    public static int clientTotal = 5000;
 
    // 同時併發執行的線程數
    public static int threadTotal = 200;
 
    private static Set<Integer> set = new ConcurrentSkipListSet<>();
 
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal; i++) {
            final int count = i;
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    update(count);
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("size:{}", set.size());
    }
 
    private static void update(int i) {
        set.add(i);
    }
}


HashMap --> ConcurrentHashMap 與 TreeMap --> ConcurrentSkipListMap

概念 :code

ConcurrentHashMap是HashMap線程安全的版本,ConcurrentHashMap不容許空值,在實際的應用中除了少數的插入操做和刪除操做外,絕大多數操做都是讀取操做,並且讀操做大多數都是成功的,基於這個前提ConcurrentHashMap針對讀操做多了特別多的優化,具備特別高的併發性;

ConcurrentSkipListMap是TreeMap線程安全的版本,ConcurrentSkipListMap底層是使用SkipList這種跳錶的結構實現的;

// ConcurrentHashMap
@Slf4j
@ThreadSafe
public class ConcurrentHashMapExample {
 
    // 請求總數
    public static int clientTotal = 5000;
 
    // 同時併發執行的線程數
    public static int threadTotal = 200;
 
    private static Map<Integer, Integer> map = new ConcurrentHashMap<>();
 
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal; i++) {
            final int count = i;
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    update(count);
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("size:{}", map.size());
    }
 
    private static void update(int i) {
        map.put(i, i);
    }
}

// ConcurrentSkipListMap
@Slf4j
@ThreadSafe
public class ConcurrentSkipListMapExample {
 
    // 請求總數
    public static int clientTotal = 5000;
 
    // 同時併發執行的線程數
    public static int threadTotal = 200;
 
    private static Map<Integer, Integer> map = new ConcurrentSkipListMap<>();
 
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal; i++) {
            final int count = i;
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    update(count);
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("size:{}", map.size());
    }
 
    private static void update(int i) {
        map.put(i, i);
    }
}

2、J.U.C的實際構成

 

 

3、安全共享對象策略總結

1 線程限制 : 一個被線程限制的對象,由線程獨佔,而且只能被佔有它的線程修改;

2 共享只讀 : 一個共享只讀的對象,在沒有額外同步的狀況下,能夠被多個線程併發訪問,可是任何線程都不能修改它;

3 線程安全對象 : 一個線程安全的對象或者容器,在內部經過同步機制來保證線程安全,因此其餘線程無需額外的同步就能夠經過公共接口隨意訪問它;

4 被守護對象 : 被守護對象只能經過獲取特定的鎖來訪問;

相關文章
相關標籤/搜索