前言
java提供了很多阻塞隊列,在平時的開發中也會使用到,所以在此對java提供的阻塞隊列進行一個瞭解總結
首先
java的阻塞隊列都繼承與BlockingQueue,其有共同的方法
boolean offer(Object o);//將數據o加入隊列中,加入成功返回true,失敗則爲false,此方法不阻塞
boolean offer(Object o,long timeout,TimeUnit unit);//將o加入隊列中,若timeout過後爲未加入成功返回false,否則返回true,此方法會阻塞等待,unit爲時間單位
put(Object o);//將數據o加入隊列中,若隊列沒有空間則會阻塞當前的線程進行等待
Object poll();//取走隊列頭部的數據,如取不出則返回null
Object poll(long timeout,TimeUnit unit);//取走隊列頭部的數據,若取不出則等待timeout,等待後取不出返回null
Object take();//取出隊列首部數據,若取不出則阻塞直到取出來
int drainTo(Collection c);//取出隊列中所有數據放到容器c中,其中的排序爲隊列中的排序,返回取出的數量
int drainTo(Collection c,int maxLength);//取出隊列的數據,最大數量爲maxLength,返回實際獲取得數量
其他隊列基本都有實現以下的方法:
peek()與poll()功能一樣
隊列有add()方法,其內部實現與put()基本都是一樣的.而且還有許多方法與列表方法一樣用的,如size(),remove(),clear(),遍歷時使用迭代器遍歷.其中remove()爲刪除隊頭
隊列的有界無界表明其是否指定或限定隊列大小
ArrayBlockingQueue
用數組實現的有界阻塞隊列,按照先進先出的原則.
public class ArrayBlockingQueueTest {
ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(5,true);//定義隊列大小爲5,隊列爲先進先出順序,false爲未指定順序
ExecutorService executorService = Executors.newSingleThreadExecutor();//單線程的線程池
public ArrayBlockingQueueTest() {
for (int i = 0;i<6;i++){
put("數據:"+i);//放入數據
}
}
public void put(String data){
try {
arrayBlockingQueue.put(data);//向隊列中放入數據
} catch (InterruptedException e) {
e.printStackTrace();
}
handle();//開啓線程處理
}
public void handle(){
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("處理中...");
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String data = null;
try {
data = arrayBlockingQueue.poll(1, TimeUnit.SECONDS);//取出數據,1秒內取不出返回null
} catch (InterruptedException e) {
e.printStackTrace();
}
if ( data != null) {
System.out.println(data+"處理結束...");
}else {
System.out.println("無數據處理...");
}
}
});
}
}
LinkedBlockingQueue
基於鏈表的阻塞隊列,按照先進先出,其加入隊列與取出隊列的線程使用獨立的鎖來控制同步,所以其有更高的併發效率,需要注意的是在初始化時如果不指定長度會默認爲無限長,這有可能會佔用較多的資源,使用方法與ArrayBlockingQueue一致.
PriorityBlockingQueue
無界優先級隊列,默認爲升序,與Arrays.sort()方法排序類似,在初始化時可以指定其初始長度,默認爲11,增長數量爲當大於等於64長度時,爲原長度的1.5被,當小於64的時候爲原長度的2倍+2.初始化時可設置其排序的比較規則Comparator(),也可重寫其compareTo方法.其無法排序同優先級
public class PriorityBlockingQueueTest {
PriorityBlockingQueue<String> priorityBlockingQueue;
String[] strings;
Comparator<String> mComparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.length() - o1.length();
}
@Override
public boolean equals(Object obj) {
return false;
}
};
public PriorityBlockingQueueTest() {
strings = new String[]{"666", "6626", "6645457645661234566", "6612423564566", "6644564564564564564564566", "664566", "664564566", "664566"};
priorityBlockingQueue = new PriorityBlockingQueue<>(11,mComparator);
priorityBlockingQueue.put("666");
priorityBlockingQueue.put("6626");
priorityBlockingQueue.put("6645457645661234566");
priorityBlockingQueue.put("6612423564566");
priorityBlockingQueue.put("6644564564564564564564566");
priorityBlockingQueue.put("664566");
priorityBlockingQueue.put("664564566");
priorityBlockingQueue.put("664566");
Iterator<String> iterator = priorityBlockingQueue.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("-----------------------------------------");
Arrays.sort(strings,mComparator);
for (String data : strings){
System.out.println(data);
}
}
}
運行結果
經試驗驗證,其內部的排序使用二分法排序,如下代碼,結果與想象的不一樣,具體原因還有待研究
private static <T> void siftUpUsingComparator(int k, T x, Object[] array,
Comparator<? super T> cmp) {
while (k > 0) {
int parent = (k - 1) >>> 1;//取中間值比較
Object e = array[parent];
if (cmp.compare(x, (T) e) >= 0)//此處遇到返回大於等於0的就退出,退出操作有點問題
break;
array[k] = e;
k = parent;
}
array[k] = x;
}
DelayQueue
延時取出的無界隊列,基於PriorityQueue實現的,加入的元素需要實現Delayed接口,目前並不會使用
SynchronousQueue
不存儲元素的隊列,當插入的一個元素,必須等待其他線程移除纔可繼續運行,當移除一個元素,如使用remove且無插入在等待會報錯,使用take()會等到有元素插入了才返回,否則阻塞,其他的有就有沒有就返回null.
public class SynchronoutsQueueTest {
SynchronousQueue<String> stringSynchronousQueue = new SynchronousQueue<>();
public SynchronoutsQueueTest() {
new MyThread().start();
try {
stringSynchronousQueue.put("666");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----------putEndMain----------");
try {
System.out.println("----------poll:"+stringSynchronousQueue.take()+"Main----------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
class MyThread extends Thread{
@Override
public void run() {
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----------poll:"+stringSynchronousQueue.poll()+"Thread----------");
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
stringSynchronousQueue.put("666");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----------putEndThread----------");
}
}
}
運行結果
LinkedTransferQueue
無界的鏈表隊列,用於生產者等待消費者的需求,其實現了TransferQueue接口,其中有幾個重要方法
transfer(Object o);//加入隊列,如加入時沒有取出隊列操作,會阻塞等待取出.
boolean tryTransfer(Object o);//加入隊列,如有取出的操作在等待則加入並返回true,否則不加入並返回false
boolean tryTransfer(Object o,long timeout,TimeUnit unit);//有超時機制的tryTransfer
public class LinkedTransferQueueTest {
LinkedTransferQueue<String> linkedTransferQueue = new LinkedTransferQueue<>();
public LinkedTransferQueueTest() {
new TestThread().start();
while (true) {
try {
Thread.sleep(3000);
System.out.println("消費:" + linkedTransferQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class TestThread extends Thread{
@Override
public void run() {
while (true) {
try {
System.out.println("出產...");
linkedTransferQueue.transfer("34636");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
運行結果
在每次出產後都會等待消費後再進行下一次的出產
LinedBlockingDeque
雙向阻塞隊列,在初始化時可設置其最大容量,默認爲int的最大值,隊列提供了addFirst、addLast、offerFirst、offerLast、peekFirst、peekLast等方法進行對隊頭和隊尾的操作。
小結
java提供的阻塞隊列使用起來也是挺便利的,雖然有些可能寫錯了,希望大家指出糾正.
個人博客:https://lewis-v.github.io/
公衆號