一、concurrentSkipListMap、concurrentSkipListSet
concurrentSkipListMap、concurrentSkipListSet是兩個有序的容器,可以理解爲是treeMap和treeSet的併發版本
skipList是一種跳錶,是以空間換時間的一種機制,數據結構如下圖所顯示
2、concurrentLinkedQueue
無界非阻塞隊列, linkedList的併發版本
3.寫時複製容器:copyOnWriteArrayList、copyOnWriteArraySet
-
寫時複製的概念
當有寫操作時,將原有容器copy一份進行操作,然後將舊容器的指針指向新的容器。所有的讀操作都是在舊容器上進行的操作。讀寫都不進行加鎖 -
數據一致性問題、只能保證最終一致性
寫線程在新的容器中寫數據時,讀線程是無法看到的,因爲操作的是不同的容器,不能保證實時的可見行,只能被保證最終的一致性 -
內存佔用問題
寫入數據的時候容器的全部對象都要進行一份拷貝 -
適合場景
讀多寫少的操作場景,比如(白名單、黑名單、商品類目的更新)舉例:百度搜索引擎有許多敏感詞,這些詞彙每天都會有大量的讀操作,但是寫操作確實很少的,可能一天有時候都會寫不到一次,使用寫時複製容器這時候的效率是很高的
4、阻塞隊列
-
概念、生產者消費者模式
生產者消費者模式如果消費者和生產者的能力不匹配時,比如消費者的處理速度要高於生產者的生產速度,這是消費者就需要等待生產者去生產數據,然後再進行消費,阻塞隊列就是幹這事的。生產者往隊列中加數據,消費者讀數據,當容器中爲空時消費者阻塞等待,當容器滿的時候,生產者阻塞等待 -
方法
-
常用的阻塞隊列辨析
1⃣️ArrayBlockingQueue:由數組結構組成的有界阻塞隊列
按照先進先出的規則、需要指定初始化容器大小、生產者消費者使用同一把鎖
2⃣️LinkedBlockingQueue:由鏈表結構組成的有界阻塞隊列
按照先進先出的規則、可以不設定初始大小,使用兩把鎖
3⃣️PriorityBlockingQueue:支持優先級排序的無界阻塞隊列
默認按照自然順序排序,否則需要重寫compareTo方法,或者初始化時指定比較器comparator
4⃣️DelayQueue:使用優先級隊列實現的無界阻塞隊列
支持延遲獲取元素的阻塞隊列,需要實現Delayed接口(超時緩存,訂單到期、限時支付)
5⃣️SynchronousQueue:不存儲元素的阻塞隊列
每一個put操作都要等待take操作,如果沒有消費者來進行take,則put操作需要阻塞
6⃣️LinkedTransferQueue:由鏈表結構組成的無界阻塞隊列
transfer():如果有消費者等待take,直接將數據給消費者,不插入隊列,消費者消費了之後當前方法纔會返回。
tryTransfer():無論消費者是否接收都會立即返回
7⃣️LinkedBlockingDeque:鏈表組成的雙向阻塞隊列 可以從隊頭和隊尾進行操作,效率更高,可實現工作密取
-
使用阻塞隊列-----實現一個延時訂單
package cn.enjoy.controller.thread.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* @author:wangle
* @description:存放到隊列的元素、延時隊列
* @version:V1.0
* @date:2020-04-04 23:16
**/
public class DelayQueueValue<T> implements Delayed {
//到期時間、單元毫秒
private Long activeTime;
private T data;
public DelayQueueValue(Long activeTime,T data){
this.data = data;
this.activeTime = TimeUnit.NANOSECONDS.convert(activeTime,TimeUnit.MILLISECONDS)+System.nanoTime();
}
public Long getActiveTime(){
return activeTime;
}
public T getData(){
return data;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.activeTime-System.nanoTime(),TimeUnit.NANOSECONDS);
}
@Override
public int compareTo(Delayed o) {
Long d = getDelay(TimeUnit.NANOSECONDS)-o.getDelay(TimeUnit.NANOSECONDS);
return d==0?0:(d>0?1:-1);
}
}
package cn.enjoy.controller.thread.DelayQueue;
/**
* @author:wangle
* @description:訂單類
* @version:V1.0
* @date:2020-04-04 23:29
**/
public class Order {
//訂單name
private final String name;
public Order(String name){
this.name=name;
}
public String getName(){
return name;
}
}
package cn.enjoy.controller.thread.DelayQueue;
import java.util.concurrent.DelayQueue;
/**
* @author:wangle
* @description:生產者
* @version:V1.0
* @date:2020-04-04 23:32
**/
public class provider extends Thread{
private DelayQueue<DelayQueueValue<Order>> delayQueue;
public provider(DelayQueue<DelayQueueValue<Order>> delayQueue){
this.delayQueue = delayQueue;
}
@Override
public void run(){
Order order = new Order("淘寶");
DelayQueueValue<Order> delayQueueValue = new DelayQueueValue<Order>(5000L,order);
delayQueue.offer(delayQueueValue);
System.out.println("淘寶5秒後到期");
Order orderLast = new Order("京東");
DelayQueueValue<Order> delayQueueValueLast = new DelayQueueValue<Order>(8000L,orderLast);
delayQueue.offer(delayQueueValueLast);
System.out.println("京東8秒後到期");
}
}
package cn.enjoy.controller.thread.DelayQueue;
import java.util.concurrent.DelayQueue;
/**
* @author:wangle
* @description:消費者
* @version:V1.0
* @date:2020-04-04 23:40
**/
public class consumer extends Thread {
private DelayQueue<DelayQueueValue<Order>> delayQueue;
public consumer(DelayQueue<DelayQueueValue<Order>> delayQueue){
this.delayQueue = delayQueue;
}
public void run(){
while(true){
try{
DelayQueueValue<Order> delayQueueValue = delayQueue.take();
Order order = delayQueueValue.getData();
System.out.println(order.getName()+"到期了");
}catch (InterruptedException e){
//TODO deal
}
}
}
}
package cn.enjoy.controller.thread.DelayQueue;
import java.util.concurrent.DelayQueue;
/**
* @author:wangle
* @description:
* @version:V1.0
* @date:2020-04-04 23:44
**/
public class TestCase {
public static void main(String[] args)throws InterruptedException{
DelayQueue<DelayQueueValue<Order>> delayQueue = new DelayQueue<>();
new provider(delayQueue).start();
new consumer(delayQueue).start();
for(int i=0;i<15;i++){
Thread.sleep(500);
System.out.println(i*500);
}
}
}
結果解析:我們向阻塞隊列中插入兩條數據,過期時間分別是5s和8s,消費者一直去take,則到期時間到了之後則會將數據從阻塞隊列中取出並進行業務操作。