Concurrent包是jdk1.5所提供的一個針對高並發進行編程的包。
1.阻塞式隊列 - BlockingQueue
遵循先進先出(FIFO)的原則。阻塞式隊列本身使用的時候是需要指定界限的。
在生產者消費者模型中,生產數據和消費數據的速率不一致,如果生產數據速度快一些,消費(處理)不過來,就會導致數據丟失。這時候我們就可以應用上阻塞隊列來解決這個問題。
ArrayBlockingQueue - 阻塞式順序隊列
底層是基於數組來進行存儲,使用的時候需要指定一個容量,容量指定之后不可改變。--- 生產 - 消費模型
public static void main(String[] args) throws InterruptedException { // 這個隊列在創建的時候需要指定容量,容量在指定之后不可變 ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(5); // 添加隊列 queue.add("a"); queue.add("b"); queue.add("c"); queue.add("d"); queue.add("e"); // 如果隊列已滿,則拋出異常 - IllegalStateException // queue.add("a"); // 返回值標記元素是否成功添加到隊列里面 // 如果隊列已滿,則返回false // boolean b = queue.offer("b"); // System.out.println(b); // 如果隊列已滿,會產生阻塞 --- 直到這個隊列中有元素被取出,才會放開阻塞 // queue.put("c"); // 定時阻塞 // 在3s之內如果有元素被取出,那么元素就會添加到隊列中 // 如果3s之后隊列依然是滿的,那么返回false表示添加失敗 boolean b = queue.offer("d", 3000, TimeUnit.MILLISECONDS); System.out.println(b); System.out.println(queue); }
LinkedBlockingQueue - 阻塞式鏈式隊列
底層是基於鏈表(節點)來進行數據的存儲。在使用的時候可以指定初始容量,也可以不指定。
如果指定了容量,就以指定的容量為准來進行存儲;
如果不指定容量,那么默認容量是 Integer.MAX_VALUE -> 231 - 1。如果不指定容量,一般認為這個容量是無限的。
PriorityBlockingQueue - 具有優先級的阻塞式隊列
如果不指定容量,默認容量是11.如果將元素取出,那么會對元素進行自然排序 --- 要求存儲的對象所對應的類必須實現Comparable,重寫compareTo方法,講比較規則寫到方法中;如果進行迭代遍歷,那么不保證排序。
SynchronousQueue - 同步隊列
只允許存儲1個元素。
2.並發映射 - ConcurrentMap
HashMap - 底層依靠數組+鏈表存儲的數據
默認初始容量是16,默認加載因子是0.75f,默認擴容每次增加一倍。本身是一個異步式線程不安全的映射
Hashtable - 同步式線程安全的映射
對外提供的方法都是同步方法
ConcurrentHashMap - 異步式線程安全的映射
在jdk1.8之前,采用分段(分桶)鎖, 分段鎖采用的是讀寫鎖機制(讀鎖:允許多個線程讀,但是不允許線程寫;寫鎖:允許一個線程寫,但是不允許線程讀);jdk1.8不再采用鎖機制,而是CAS(Compare and Swap)算法, 減小了鎖的開銷;如果一個桶中的元素個數超過了8個,那么會將這個桶的鏈表扭轉成一棵紅黑樹(自平衡二叉查找樹)結構。
ConcurrentNavigableMap - 並發導航映射
本身是一個接口,所以更多的是使用實現類
ConcurrentSkipListMap - 並發跳躍表映射
跳躍表:為了提高查詢效率所產生的一種數據結構
跳躍表是典型的以空間換時間的產物。
跳躍表的時間復雜度是O(logn)

如果跳躍表中插入新的元素,新的元素是否往上提取,遵循“拋硬幣”原則 --- 1/2原則
只要保證這個節點有一半的概率被提取就可以
跳躍表適合大量查詢而不增刪的場景
鎖
CountDownLatch - 閉鎖 - 線程減鎖
對線程neg進行計數,當計數歸零的時候會開放阻塞線程繼續往下執行

public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { CountDownLatch cdl = new CountDownLatch(5); new Thread(new Teacher(cdl)).start(); new Thread(new Student(cdl)).start(); new Thread(new Student(cdl)).start(); new Thread(new Student(cdl)).start(); new Thread(new Student(cdl)).start(); // 表示讓線程阻塞,直到計數歸零的時候阻塞才能放開 cdl.await(); System.out.println("考試結束~~~"); } } class Student implements Runnable { private CountDownLatch cdl; public Student(CountDownLatch cdl) { this.cdl = cdl; } @Override public void run() { System.out.println("學生來到考場,准備考試~~~"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("學生交卷離開考場"); // 計數-1 cdl.countDown(); } } class Teacher implements Runnable { private CountDownLatch cdl; public Teacher(CountDownLatch cdl) { this.cdl = cdl; } @Override public void run() { System.out.println("老師來到考場,准備考試~~~"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("老師收卷離開了考場~~~"); cdl.countDown(); } }
