多執行緒同步集合和併發集合
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 多執行緒(三)--Lock和Condition實現執行緒同步通訊
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.
併發程式設計之多執行緒基礎-Thread和Runnable的區別及聯絡(二)
上篇文章講述了建立執行緒的常用方式 本篇主要分析一下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