JAVA併發隊列

Java併發隊列


在併發隊列上JDK提供了兩套實現: 
一個是以ConcurrentLinkedQueue爲代表的高性能隊列; 
一個是以BlockingQueue接口爲代表的阻塞隊列; 
無論哪種都繼承自Queue。 
這裏寫圖片描述

一、ConcurrentLinkedQueue

定義

ConcurrentLinkedQueue : 是一個適用於高併發場景下的隊列,通過無鎖的方式,實現了高併發狀態下的高性能,通常ConcurrentLinkedQueue性能好於BlockingQueue。 
它是一個基於鏈接節點的無界線程安全隊列。該隊列的元素遵循先進先出的原則。 
頭是最先加入的,尾是最近加入的,該隊列不允許null元素。

ConcurrentLinkedQueue重要方法:

add 和offer() :都是加入元素的方法(在ConcurrentLinkedQueue中這倆個方法沒有任何區別) 
poll() 和peek() :都是取頭元素節點,區別在於前者會刪除元素,後者不會。

代碼示例:

ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();

q.offer("張三");

q.offer("李四");

q.offer("王五");

q.offer("趙六");

q.offer("大聖");

//從頭獲取元素,刪除該元素

System.out.println(q.poll());

//從頭獲取元素,不刪除該元素

System.out.println(q.peek());

//獲取總長度

System.out.println(q.size());

二、BlockingQueue

定義: 
阻塞隊列(BlockingQueue)是一個支持兩個附加操作的隊列。這兩個附加的操作是: 
1、在隊列爲空時,獲取元素的線程會等待隊列變爲非空。 
2、當隊列滿時,存儲元素的線程會等待隊列可用。 
阻塞隊列是線程安全的。 
用途: 
阻塞隊列常用於生產者和消費者的場景,生產者是往隊列裏添加元素的線程,消費者是從隊列裏拿元素的線程。阻塞隊列就是生產者存放元素的容器,而消費者也只從容器裏拿元素。

1、ArrayBlockingQueue

重要方法: 
具體看在線文檔:jdk在線中文文檔

add(E e) 
將指定的元素插入到此隊列的尾部(如果立即可行且不會超過該隊列的容量),在成功時返回 true,如果此隊列已滿,則拋出IllegalStateException; 
offer(E e) 
將指定的元素插入到此隊列的尾部(如果立即可行且不會超過該隊列的容量),在成功時返回 true,如果此隊列已滿,則返回 false。 
offer(E e, long timeout, TimeUnit unit) 
將指定的元素插入此隊列的尾部,如果該隊列已滿,則在到達指定的等待時間之前等待可用的空間。

定義:

ArrayBlockingQueue是一個有邊界的阻塞隊列,它的內部實現是一個數組。 
有邊界的意思是它的容量是有限的,我們必須在其初始化的時候指定它的容量大小,容量大小一旦指定就不可改變。 
ArrayBlockingQueue是以先進先出的方式存儲數據,最新插入的對象是尾部,最新移出的對象是頭部。

下面是一個初始化和使用ArrayBlockingQueue的例子:

//初始化3個隊列

ArrayBlockingQueue array = new ArrayBlockingQueue(3);

array.add("張三");

array.add("李四");

array.add("大聖");

// 添加阻塞隊列

boolean a=array.offer("王五",1, TimeUnit.SECONDS);

System.out.println(a);

//運行結果:false

 

2、LinkedBlockingQueue

定義:

LinkedBlockingQueue阻塞隊列大小的配置是可選的, 
如果我們初始化時指定一個大小,它就是有邊界的,如果不指定,它就是無邊界的。 
說是無邊界,其實是採用了默認大小爲Integer.MAX_VALUE的容量 。它的內部實現是一個鏈表。 
和ArrayBlockingQueue一樣,LinkedBlockingQueue 也是以先進先出的方式存儲數據,最新插入的對象是尾部,最新移出的對象是頭部。

下面是一個初始化和使LinkedBlockingQueue的例子:

//初始化

LinkedBlockingQueue lbq = new LinkedBlockingQueue(3);

lbq.add("張三");

lbq.add("李四");

lbq.add("李四");

System.out.println(lbq.size());

//運行結果:3

3、PriorityBlockingQueue

定義:

PriorityBlockingQueue是一個沒有邊界的隊列,它的排序規則和 java.util.PriorityQueue一樣。需要注意,PriorityBlockingQueue中允許插入null對象。 
所有插入PriorityBlockingQueue的對象必須實現 java.lang.Comparable接口,隊列優先級的排序規則就是按照我們對這個接口的實現來定義的。 
另外,我們可以從PriorityBlockingQueue獲得一個迭代器Iterator,但這個迭代器並不保證按照優先級順序進行迭代。

4、SynchronousQueue

定義:

SynchronousQueue隊列內部僅允許容納一個元素。當一個線程插入一個元素後會被阻塞,除非這個元素被另一個線程消費。

三、使用BlockingQueue模擬生產者與消費者

代碼示例

生產者:

public class ProducerThread implements Runnable {

private BlockingQueue queue;

private volatile boolean flag = true;

private static AtomicInteger count = new AtomicInteger();


public ProducerThread(BlockingQueue queue) {

this.queue = queue;

}


@Override

public void run() {

try {

System.out.println("生產線程啓動...");

while (flag) {

System.out.println("正在生產數據....");

String data = count.incrementAndGet()+"";

// 將數據存入隊列中

boolean offer = queue.offer(data, 2, TimeUnit.SECONDS);

if (offer) {

System.out.println("生產者,存入" + data + "到隊列中,成功.");

} else {

System.out.println("生產者,存入" + data + "到隊列中,失敗.");

}

Thread.sleep(1000);

}

} catch (Exception e) {


} finally {

System.out.println("生產者退出線程");

}


}

public void stopThread() {

this.flag = false;

}

}

消費者:

class ConsumerThread implements Runnable {

private BlockingQueue<String> queue;

private volatile boolean flag = true;


public ConsumerThread(BlockingQueue<String> queue) {

this.queue = queue;


}


@Override

public void run() {

System.out.println("消費線程啓動...");

try {

while (flag) {

System.out.println("消費者,正在從隊列中獲取數據..");

String data = queue.poll(2, TimeUnit.SECONDS);

if (data != null) {

System.out.println("消費者,拿到隊列中的數據data:" + data);

Thread.sleep(1000);

} else {

System.out.println("消費者,超過2秒未獲取到數據..");

flag = false;

}



}

} catch (Exception e) {

e.printStackTrace();

} finally {

System.out.println("消費者退出線程...");

}


}


}

運行:

public class ProducerAndConsumer {

public static void main(String[] args) throws InterruptedException {

BlockingQueue<String> queue = new LinkedBlockingQueue<String>(10);

ProducerThread producerThread1 = new ProducerThread(queue);

ProducerThread producerThread2 = new ProducerThread(queue);

ConsumerThread consumerThread1 = new ConsumerThread(queue);

Thread t1 = new Thread(producerThread1);

Thread t2 = new Thread(producerThread2);

Thread c1 = new Thread(consumerThread1);

t1.start();

t2.start();

c1.start();


// 執行2s後,生產者不再生產

Thread.sleep(2* 1000);

producerThread1.stopThread();

producerThread2.stopThread();


}

}

運行結果: 
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章