blockingQueue實爲阻塞隊列,數據由消費者線程、生產者線程共享。消費者產生數據放入阻塞隊列,倘若阻塞隊列已滿,則生產者線程被阻塞直到消費者線程取出數據或者是直接返回失敗,若隊列數據爲空,則消費者線程阻塞直到生產者線程放入數據或者直接返回失敗。且阻塞隊列的數據爲先進先出的規則。
常用api
offer(E e) 向阻塞隊列放入數據,倘若阻塞隊列已滿,則直接返回false。
offer(E e, long timeout, TimeUnit unit) 向阻塞隊列放入數據,倘若timeout時間過後數據還未放入隊列,則直接返回false。
put(E e) 向阻塞隊列放入數據,倘若阻塞隊列已滿,則生產線程阻塞直到消費線程取出數據。
poll() 向阻塞隊列取出數據,倘若阻塞隊列沒有數據,則直接返回false。
poll(long timeout, TimeUnit unit) 向阻塞隊列取出數據,倘若timeout時間過後依然沒有取出數據,則直接返回false。
take() 向阻塞隊列放入數據,倘若阻塞隊列已滿,則消費線程阻塞直到生產線程放入數據。
drainTo():一次性從阻塞隊列所有可用的數據對象。
blockingQueue常用實現類
ArrayBlockingQueue 採用數組來放入存儲數據。在初始化的時候需要指定數組長度。這就限定了ArrayBlockingQueue 只能存儲指定長度的數據。並且ArrayBlockingQueue 的放入與取出不能併發進行,因爲ArrayBlockingQueue 採用唯一的鎖來維護存放隊列的數組。適合生產線程產生的數據快於消費線程消費數據的場景。
LinkedBlockingQueue 採用鏈表來維護數據隊列。因爲鏈表增加與取出數據對象是作用在不同對象上,所以取出與增加是可以併發進行的。但是LinkedBlockingQueue 初始化的時候可以不限定長度,這就導致了LinkedBlockingQueue 可以存儲接近於無窮的數據,這可能會導致內存溢出。所有LinkedBlockingQueue 適合生產線程產生的數據慢於消費線程消費數據的場景。
阻塞隊列使用場景
列如用戶上傳了一個數據量很大的文件,後臺解析需要耗費十幾秒,這時總不能讓用戶等待十幾秒吧,這時候只需將文件數據放入阻塞隊列,然後放回給用戶成功,接着生產線程去處理這些數據。
例如12306開票的時候大量用戶湧進來買票,這時候由於高併發量太大,導致很多業務邏輯需要同時處理,數據庫io操作過於密集頻繁,這時候可以把用戶的請求數據放入消息隊列進行排隊,相關接口去取數據用於業務操作。
例如12306訂票成功後需要發送郵件通知給用戶,由於12306的系統能應對很強大的高併發場景,但是郵件系統不能處理大量高併發。由於發送郵件可以延遲,所有隻需要把發送郵件的內容放入阻塞隊列,發郵件線程去取數據發送即可。
使用ArrayBlockingQueue實現消費-生產情景
package TestExample;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Test;
public class TestExample{
ArrayBlockingQueue<String> test = new ArrayBlockingQueue<String>(10);
CountDownLatch latch = new CountDownLatch(2);
@Test
public void test(){
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new ProductTread(test));
service.execute(new ConsumeTread(test));
try {
latch.await();
System.out.println("主線程結束!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
class ProductTread implements Runnable{
BlockingQueue<String> bq;
public void run() {
for(int i=0;i<20;i++){
try {
bq.put(String.valueOf(i));
System.out.println("生產者放入數據:"+String.valueOf(i));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
latch.countDown();
}
ProductTread(BlockingQueue<String> bq){
this.bq=bq;
}
}
class ConsumeTread implements Runnable{
BlockingQueue<String> bq;
public void run() {
for(int i=0;i<20;i++){
try {
Thread.sleep(1000);
System.out.println("消費者取出數據"+bq.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
latch.countDown();
}
ConsumeTread(BlockingQueue<String> bq){
this.bq=bq;
}
}
}
運行結果:
生產者放入數據:0
生產者放入數據:1
生產者放入數據:2
生產者放入數據:3
生產者放入數據:4
生產者放入數據:5
生產者放入數據:6
生產者放入數據:7
生產者放入數據:8
生產者放入數據:9
消費者取出數據0
生產者放入數據:10
消費者取出數據1
生產者放入數據:11
消費者取出數據2
生產者放入數據:12
消費者取出數據3
生產者放入數據:13
消費者取出數據4
生產者放入數據:14
消費者取出數據5
生產者放入數據:15
消費者取出數據6
生產者放入數據:16
消費者取出數據7
生產者放入數據:17
消費者取出數據8
生產者放入數據:18
消費者取出數據9
生產者放入數據:19
消費者取出數據10
消費者取出數據11
消費者取出數據12
消費者取出數據13
消費者取出數據14
消費者取出數據15
消費者取出數據16
消費者取出數據17
消費者取出數據18
消費者取出數據19
主線程結束!