1. 程式人生 > >多執行緒同步集合和併發集合

多執行緒同步集合和併發集合

Java多執行緒之同步集合和併發集合

不管是同步集合還是併發集合他們都支援執行緒安全,他們之間主要的區別體現在效能和可擴充套件性,還有他們如何實現的執行緒安全。

同步集合類

  • Hashtable
  • Vector
  • 同步集合包裝類,Collections.synchronizedMap()和Collections.synchronizedList() 

併發集合類

  • ConcurrentHashMap
  • CopyOnWriteArrayList
  • CopyOnWriteHashSet

效能

      同步集合比並發集合會慢得多,主要原因是鎖,同步集合會對整個May或List加鎖

併發集合的實現原理

  • ConcurrentHashMap:把整個Map 劃分成幾個片段,只對相關的幾個片段上鎖,同時允許多執行緒訪問其他未上鎖的片段。
  • CopyOnWriteArrayList:允許多個執行緒以非同步的方式讀,當有執行緒寫的時候它會將整個List複製一個副本給它。如果在讀多寫少這種對併發集合有利的條件下使用併發集合,這會比使用同步集合更具有可伸縮性。

併發集合的使用建議

  • 一般不需要多執行緒的情況,只用到HashMap、ArrayList,只要真正用到多執行緒的時候就一定要考慮同步。所以這時候才需要考慮同步集合或併發集合。

ConcurrentHashMap實現原理

       ConcurrentHashMap是由Segment陣列結構和HashEntry陣列結構組成。Segment是一種可重入鎖ReentrantLock,在ConcurrentHashMap裡扮演鎖的角色,HashEntry則用於儲存鍵值對資料。一個ConcurrentHashMap裡包含一個Segment陣列,Segment的結構和HashMap類似,是一種陣列和連結串列結構, 一個Segment裡包含一個HashEntry陣列,每個HashEntry是一個連結串列結構的元素, 每個Segment守護者一個HashEntry數組裡的元素,當對HashEntry陣列的資料進行修改時,必須首先獲得它對應的Segment鎖。

什麼是CopyOnWrite容器

       CopyOnWrite容器即寫時複製的容器。通俗的理解是當我們往一個容器新增元素的時候,不直接往當前容器新增,而是先將當前容器進行Copy,複製出一個新的容器,然後新的容器裡新增元素,新增完元素之後,再將原容器的引用指向新的容器。這樣做的好處是我們可以對CopyOnWrite容器進行併發的讀,而不需要加鎖,因為當前容器不會新增任何元素。所以CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不同的容器。

CopyOnWriteArrayList的實現原理

可以發現在新增的時候是需要加鎖的,否則多執行緒寫的時候會Copy出N個副本出來。

Java程式碼 

public boolean add(T e) {  
    final ReentrantLock lock = this.lock;  
    lock.lock();  
    try {  
  
        Object[] elements = getArray();  
  
        int len = elements.length;  
  
        // 複製出新陣列  
        Object[] newElements = Arrays.copyOf(elements, len + 1);  
  
        // 把新元素新增到新數組裡  
        newElements[len] = e;  
  
        // 把原陣列引用指向新陣列  
        setArray(newElements);  
  
        return true;  
  
    } finally {  
        lock.unlock();  
    }  
}  
  
  
final void setArray(Object[] a) {  
  
    array = a;  
  
}  

讀的時候不需要加鎖,如果讀的時候有多個執行緒正在向ArrayList新增資料,讀還是會讀到舊的資料,因為寫的時候不會鎖住舊的ArrayList。

Java程式碼 :

public E get(int index) {  
    return get(getArray(), index);  
}  

JDK中並沒有提供CopyOnWriteMap,我們可以參考CopyOnWriteArrayList來實現一個,基本程式碼如下:
Java程式碼 :


import java.util.Collection;  
import java.util.Map;  
import java.util.Set;  
   
public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {  
    private volatile Map<K, V> internalMap;  
   
    public CopyOnWriteMap() {  
        internalMap = new HashMap<K, V>();  
    }  
   
    public V put(K key, V value) {  
   
        synchronized (this) {  
            Map<K, V> newMap = new HashMap<K, V>(internalMap);  
            V val = newMap.put(key, value);  
            internalMap = newMap;  
            return val;  
        }  
    }  
   
    public V get(Object key) {  
        return internalMap.get(key);  
    }  
   
    public void putAll(Map<? extends K, ? extends V> newData) {  
        synchronized (this) {  
            Map<K, V> newMap = new HashMap<K, V>(internalMap);  
            newMap.putAll(newData);  
            internalMap = newMap;  
        }  
    }  
}  

CopyOnWrite的應用場景

       CopyOnWrite併發容器用於讀多寫少的併發場景。比如白名單,黑名單,商品類目的訪問和更新場景,假如我們有一個搜尋網站,使用者在這個網站的搜尋框中,輸入關鍵字搜尋內容,但是某些關鍵字不允許被搜尋。這些不能被搜尋的關鍵字會被放在一個黑名單當中,黑名單每天晚上更新一次。當用戶搜尋時,會檢查當前關鍵字在不在黑名單當中,如果在,則提示不能搜尋。實現程式碼如下:

import java.util.Map;  
import com.ifeve.book.forkjoin.CopyOnWriteMap;  
  
/** 
 * 黑名單服務 
 * 
 * @author fangtengfei 
 * 
 */  
public class BlackListServiceImpl {  
   
    private static CopyOnWriteMap<String, Boolean> blackListMap = new CopyOnWriteMap<String, Boolean>(  
            1000);  
   
    public static boolean isBlackList(String id) {  
        return blackListMap.get(id) == null ? false : true;  
    }  
   
    public static void addBlackList(String id) {  
        blackListMap.put(id, Boolean.TRUE);  
    }  
   
    /** 
     * 批量新增黑名單 
     * 
     * @param ids 
     */  
    public static void addBlackList(Map<String,Boolean> ids) {  
        blackListMap.putAll(ids);  
    }  
}  

注意兩點:

  • 減少擴容開銷。根據實際需要,初始化CopyOnWriteMap的大小,避免寫時CopyOnWriteMap擴容的開銷。
  • 使用批量新增。因為每次新增,容器每次都會進行復制,所以減少新增次數,可以減少容器的複製次數。如使用上面程式碼裡的addBlackList方法。

CopyOnWrite的缺點

記憶體佔用問題

因為CopyOnWrite的寫時複製機制,所以在進行寫操作的時候,記憶體裡會同時駐紮兩個物件的記憶體,舊的物件和新寫入的物件(注意:在複製的時候只是複製容器裡的引用,只是在寫的時候會建立新物件新增到新容器裡,而舊容器的物件還在使用,所以有兩份物件記憶體)。如果這些物件佔用的記憶體比較大,比如說200M左右,那麼再寫入100M資料進去,記憶體就會佔用300M,那麼這個時候很有可能造成頻繁的Yong GC和Full GC。之前我們系統中使用了一個服務由於每晚使用CopyOnWrite機制更新大物件,造成了每晚15秒的Full GC,應用響應時間也隨之變長。

針對記憶體佔用問題,可以通過壓縮容器中的元素的方法來減少大物件的記憶體消耗,比如,如果元素全是10進位制的數字,可以考慮把它壓縮成36進位制或64進位制。或者不使用CopyOnWrite容器,而使用其他的併發容器,如ConcurrentHashMap。

資料一致性問題

CopyOnWrite容器只能保證資料的最終一致性,不能保證資料的實時一致性。所以如果你希望寫入的的資料,馬上能讀到,請不要使用CopyOnWrite容器。

相關推薦

java 執行 同步 觀察者 併發集合的一個例子

//第一版 package com.hra.riskprice; import com.hra.riskprice.SysEnum.Factor_Type; import org.springframework.boot.SpringApplication; import org.springfram

Java執行 阻塞佇列併發集合

本章主要探討在多執行緒程式中與集合相關的內容。在多執行緒程式中,如果使用普通集合往往會造成資料錯誤,甚至造成程式崩潰。Java為多執行緒專門提供了特有的執行緒安全的集合類,通過下面的學習,您需要掌握這些集合的特點是什麼,底層實現如何、在何時使用等問題。 3.1Blockin

(WCF) 執行 (Multi-threading) 併發性 (Concurency)

問題:WCF 有個Server端,還有個Client端,他們之間是如何進行併發,多執行緒通訊的呢?多個Client端同時訪問Server,如何保證Server端的操作執行緒安全呢?   在理解WCF Concurency之前,首先需要理解 WCF instance management &nb

java學習第十二天之執行死鎖併發

package MoreThreadLearn; /* 兩個儲戶到銀行存錢,每個人存了三次,一次100元 1、描述銀行 2、描述儲戶業務 分析多執行緒是否存在安全隱患? 1、執行緒任務中是否有共享的資料 2、是否多條操作共享資料的程式碼 */ public

Python的執行效能問題併發問題

原文標題:一行 Python 實現並行化 -- 日常多執行緒操作的新思路 原文地址:http://www.zhangzhibo.net/2014/02/01/parallelism-in-one-line/ Python 在程式並行化方面多少有些聲名狼藉。撇開技術上的問

【轉】Java執行-同步集合併發集合

同步集合可以簡單地理解為通過synchronized來實現同步的集合。如果有多個執行緒呼叫同步集合的方法,它們將會序列執行。 arrayList和vector、stack Vector是執行緒安全的,原始碼中有很多的synchronized可以看出,而

執行同步集合併發集合

Java多執行緒之同步集合和併發集合 不管是同步集合還是併發集合他們都支援執行緒安全,他們之間主要的區別體現在效能和可擴充套件性,還有他們如何實現的執行緒安全。 同步集合類 Hashtable Vector 同步集合包裝類,Collections.synchroni

Java併發程式設計(8):執行環境中安全使用集合API(含程式碼)

Java併發程式設計(8):多執行緒環境中安全使用集合API(含程式碼)JAVA大資料中高階架構 2018-11-09 14:44:47在集合API中,最初設計的Vector和Hashtable是多執行緒安全的。例如:對於Vector來說,用來新增和刪除元素的方法是同步的。如果只有一個執行緒與Vector的例

【Java併發程式設計】之八:執行環境中安全使用集合API(含程式碼)

在集合API中,最初設計的Vector和Hashtable是多執行緒安全的。例如:對於Vector來說,用來新增和刪除元素的方法是同步的。如果只有一個執行緒與Vector的例項互動,那麼,要求獲取

Java執行-44-靜態非靜態方法同步鎖物件是什麼

前面一篇,我們知道了synchronized關鍵字擴起來範圍的程式碼塊就可以實現同步,其實,在Java中,只需要在方法上加上synchronized關鍵字即可,就像加上static一樣。本篇來看看加上synchronized關鍵字修飾的非靜態和靜態方法的同步鎖物件是什麼。 1.非靜態同步鎖物

Java5 執行(三)--LockCondition實現執行同步通訊

  1<Lock:   Lock比傳統執行緒模型中的Synchronied方式更加面向物件,與生活中的鎖類似,鎖本身也應該是一個物件.兩個執行緒執行的程式碼段要實現同步互斥的效果,它們必須用同一個Lock物件,鎖是在代表要操作的資源的類的內部方法中,而不是

執行同步之——兩個執行序列順序列印奇數偶數的兩種實現

題目:一道經典的執行緒併發的問題,執行緒a列印1、3、5……,執行緒b列印2、4、6……,兩個執行緒交替執行輸出1、2、3、4、5、6…… 要點: package com.test; import java.util.concurrent.locks.

併發程式設計之執行基礎-ThreadRunnable的區別及聯絡(二)

上篇文章講述了建立執行緒的常用方式 本篇主要分析一下Thread和Runnable兩種方式建立執行緒的區別及聯絡 聯絡: ▶Thread類實現了Runable介面。 ▶都需要重寫裡面Run方法。 區別: ▶Thread方式不支援多繼承,Runnable方式支援多個實現 ▶Runnable更容易實

Day 9——java執行2及字元編碼集合

java.lang.Runnable 介面 Runnable中有Public void run();方法 供現有Runnable物件建立執行緒 使用Runnable物件建立執行緒 New Thread(Runnable r).start(); 靜態同步方法

javaSE (三十五)執行執行實現方法區別、同步程式碼塊方法(執行安全))

主要還是熟悉api,熟悉方法,簡單,需要多實踐 1、 多執行緒實現方法和區別: 多執行緒實現的兩種方法: 1)類繼承Thread類或實現Runnable介面,重寫run()方法 2)建立Thread的子類物件(需要開幾個執行緒就建立幾個物件,可建立匿名內部類) 3)子類

java 執行處理一個list的集合

2016年08月03日 09:16:20 package A; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import j

程序間通訊方式執行同步機制總結

多程序之間通訊方式:           檔案對映:本地之間           共享記憶體:本地之間           匿名管道:本地之間           命名管道:跨伺服器           郵件槽:一對多的傳輸資料,通常通過網路向一臺Windo

Java執行同步非同步詳解

1. 多執行緒併發時,多個執行緒同時請求同一資源,必然導致此資源的資料不安全。 2. 執行緒池 在WEB服務中,對於web伺服器的響應速度必須儘可能的快,這就容不得在使用者提交請求按鈕後,再建立執行緒提供服務。為了減少使用者的等待時間,執行緒必須預先建立,放線上程池中,執行

python併發程式設計之程序、執行、非同步協程

一、多執行緒   多執行緒就是允許一個程序記憶體在多個控制權,以便讓多個函式同時處於啟用狀態,從而讓多個函式的操作同時執行。即使是單CPU的計算機,也可以通過不停地在不同執行緒的指令間切換,從而造成多執行緒同時執行的效果。   多執行緒相當於一個併發(concunrr

執行面試題答案:執行鎖+執行池+執行同步

多執行緒面試題和答案:執行緒鎖+執行緒池+執行緒同步 1、併發程式設計三要素? 2、多執行緒的價值? 3、建立執行緒的有哪些方式?區別是什麼? 4、建立執行緒的三種方式的對比? 4、執行緒的生命週期及五種基本狀態及轉換條件 1、J