Concurrent包详解及使用场景
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();
}
}
原文地址:https://www.cnblogs.com/chuijingjing/p/10072912.html
總結
以上是生活随笔為你收集整理的Concurrent包详解及使用场景的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何实现Linux+Windows双系统
- 下一篇: 怎么创建具有真实纹理的CG场景岩石?