使用synchronized的集合、CopyOnWriteArrayList集合、CopyOnWriteArraySet集合、ConcurrentHashMap集合、Queue接口
14.5 線程安全的集合
14.5.1 線程安全集合圖
- Collection體系集合下,除Vector以外的線程安全集合;
14.5.2 Collections中的工具方法
Collections工具類中提供了多個可以獲得線程安全集合的方法;
- public static Collection synchronizedCollection(Collection
c) - public static List synchronizedList(List list)
- public static Set synchronizedSet(Set s)
- public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
- public static SortedSet synchronizedSortedSet(SortedSet s)
- public static <K,V> SortedMap<K,V>
- synchronizedSortedMap(SortedMap<K,V> m)
JDK1.2提供,接口統一、維護性高,但性能沒有提升,均以synchronized實現;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class TestCollectionsForSyn {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
//傳入一個線程不安全的List,返回一個線程安全的List
List<String> safeList = Collections.synchronizedList(list);
//和普通集合應用無差別
safeList.add("A");//SynchronizedList裏的add方法,該方法里加了個鎖
safeList.add("B");
safeList.add("C");
safeList.remove(1);
safeList.get(1);
Set<String> set = new HashSet<String>();
Set<String> newSet = Collections.synchronizedSet(set);
newSet.add("A");
newSet.add("B");
newSet.add("C");
for(String s : newSet) {
System.out.println(s);
}
}
}
14.5.3 CopyOnWriteArrayList
- 線程安全的ArrayList,加強版讀寫分離;
- 寫有鎖,讀無鎖,讀寫之間不阻塞,優於讀寫鎖;
- 寫入時,先copy一個容器副本、再添加新元素,最後替換引用;
- 使用方法與ArrayList無異;
14.5.4 CopyOnWriteArraySet
- 線程安全的Set,底層使用CopyOnWriteArrayList實現;
- 唯一不同在於,使用addIfAbsent()添加元素,會遍歷數組;
- 如存在元素,則不添加(扔掉副本);
14.5.5 ConcurrentHashMap
- 初始容量默認爲16段(Segment),使用分段鎖設計;
- 不對整個Map加鎖,而是爲每個Segment加鎖;
- 當多個對象存入同一個Segment時,才需要互斥;
- 最理想狀態爲16個對象分別存入16個Segment,並行數量16;
- 使用方式與HashMap無異;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
public class TestCopyOnWriteArrayList {
public static void main(String[] args) {
//寫有鎖,讀無鎖的集合
CopyOnWriteArrayList<String> alist = new CopyOnWriteArrayList<String>();
//寫操作
alist.add("A");//都將底層數組做了一次複製,寫的是新數組,完成賦值後,再將新數組替換掉舊數組
alist.add("B");//沒調用一次,底層方法擴容一次
//讀操作,無鎖
alist.get(1);//讀的是寫操作完成之前的舊數組,寫完之後,才能讀到新數組的新值
//無序,無下標,不允許重複
CopyOnWriteArraySet<String> aset = new CopyOnWriteArraySet<String>();
//寫操作,表面使用的是add方法;底層實際是用的CopyOnWriteArrayList的addIfAbsent()來判斷要插入的新值是否存在
aset.add("A");
aset.add("B");
aset.add("C");
for(String s : aset) {
System.out.println(s);
}
HashMap<String , String> maps = new HashMap<String , String>();
//分段鎖設計 Segment JDK1.7的做法
ConcurrentHashMap<String , String> ch = new ConcurrentHashMap<String , String>();
//1.8的做法 CAS交換算法和同步鎖 同步鎖鎖的是表頭對象(鏈表的第一個),拿到鎖的對象要先做節點遍歷,查看有沒有相同的key,相同覆蓋,不同,則掛在最後一個節點的next上
ch.put("A", "1");
ch.keySet();
}
}
14.5.6 Queue接口(隊列)
- Collection的子接口,表示隊列FIFO(First In First Out);
常用方法:
- 拋出異常:
boolean add(E e) //順序添加一個元素(到達上限後,再添加則會拋出異常)
E remove() //獲得第一個元素並移除(如果隊列沒有元素時,則拋出異常)
E element() //獲得第一個元素但不移除(如果隊列沒有元素時,則拋出異常) - 返回特殊值:推薦使用
boolean offer(E e) //順序添加一個元素(到達上限後,再添加則會返回false)
E poll() //獲得第一個元素並移除(如果隊列沒有元素時,則返回null)
E keep() //獲得第一個元素但不移除(如果隊列沒有元素時,則返回null)
14.5.7 ConcurrentLinkedQueue
- 線程安全、可高效讀寫的隊列,高併發下性能最好的隊列;
- 無鎖、CAS比較交換算法,修改的方法包含三個核心參數(V,E,N);
- V : 要更新的變量、E:預期值、N:新值;
- 只有當V == E 時,V = N ; 否則表示已被更新過,則取消當前操作;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
public class TestQueue {
public static void main(String[] args) {
//Queue
//列表,尾部添加(指定下標添加)
//鏈表,頭尾添加
//隊列,FIFO
//Queue<String> qu = new LinkedList<String>();//遵循隊列規則的鏈表
LinkedList<String> link = new LinkedList<String>();
link.offer("A");
link.offer("B");
link.offer("C");
//用列表的方式打亂了FIFO隊列
link.add(0,"D");
//強制LinkedList後,不能調用帶有下標的add方法
System.out.println(link.peek());//隊列中的第一個元素
//嚴格遵守理論隊列的規則,且是線程安全的,採用了CAS交換算法
Queue<String> q = new ConcurrentLinkedQueue<String>();
//1.拋出異常的 2.返回結果的
q.offer("A");
q.offer("B");
q.offer("C");
q.poll();//刪除表頭
System.out.println(q.peek());//獲得表頭
}
}
14.5.8 BlockingQueue接口(阻塞隊列)
- Queue的子接口,阻塞的隊列,增加了兩個線程狀態爲無期限等待的方法;
方法:
- void put(E e) //將指定元素插入此隊列中,如果沒有可用空間,則等待;
- E take() // 獲取並移除此隊列頭部元素,如果沒有可用元素,則等待;
- 可用於解決生產者、消費者問題;
14.5.9 阻塞隊列
-
ArrayBlockingQueue:
數組結構實現,有界隊列(手工固定上限) -
LinkedBlockingQueue:
鏈表結構實現,無界隊列(默認上限Integer.MAX_VALUE)
14.6 總結
- ExecutorSevice線程池接口、Executors工廠;
- Callable線程任務、Future異步返回值;
- Lock、ReentrantLock重入鎖、ReentrantReadWriteLock讀寫鎖;
- CopyOnWriteArrayList線程安全的ArrayList;
- CopyOnWriteArraySet線程安全的Set;
- ConcurrentHashMap線程安全的HashMap;
- ConcurrentLinkedQueue線程安全的Queue;
- ArrayBlockingQueue線程安全的阻塞Queue;(生產者、消費者)