學習Java第三十天--多線程之線程安全的集合

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;(生產者、消費者)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章