Java併發編程之阻塞隊列

ArrayBlockingQueue:是一個基於數組結構的有界阻塞隊列,此隊列按FIFO(先進先出)原則對元素進行排序。

LinkedBlockingQueue:是一個基於鏈表結構的阻塞隊列,此隊列按FIFO(先進先出)排序元素,吞吐量通常高於ArrayBlockingQueue。

SynchronousQueue:一個不存儲元素的阻塞隊列,每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於SynchronousQueue。

 

 

阻塞隊列的好處


在多線程領域:所謂阻塞,在某些情況下會掛起線程,一旦條件滿足,被掛起的線程又會被自動喚醒。

 

爲什麼需要BlockingQueue

好處是我們不需要關心什麼時候需要阻塞線程,什麼時候需要喚醒線程,因爲一切BlockingQueue都給你一手包辦了。

在Concurrent包發佈以前,在多線程環境下,我們每個程序員都必須去自己控制這些細節,尤其還要兼顧效率和線程安全,這會給我們的程序帶來不小的複雜度。

 

 

阻塞隊列的核心方法


方法類型

拋出異常

特殊值

阻塞

超時

插入

add(e)

offer(e)

put(e)

offer(e,time,unit)

移除

remove()

poll()

take()

poll(time,uni)

檢查

element()

peek()

不可用

不可用

 

拋出異常

當阻塞隊列滿時,再往隊列裏add插入元素會拋出IllegalStateException: Queue full

當阻塞隊列空時,再往隊列裏remove移除元素會拋出NoSuchElementException

當阻塞隊列爲空時,使用element判斷第一個元素時拋出NoSuchElementException

特殊值

offer(),插入方法,成功true失敗false

poll(),移除方法,成功返回出隊列的元素,隊列中沒有元素就返回null

一直阻塞

當阻塞隊列滿時,生產者線程繼續往隊列裏put元素,隊列會一直阻塞,直到put或響應中斷退出

當阻塞隊列爲空時,消費者線程試圖從隊列裏take元素,隊列會一直阻塞消費者線程,直到隊列可用

超時退出

當阻塞隊列滿時,隊列會阻塞生產者線程一定時間,超過時間後線程會退出

 

隊列滿時add

BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(1);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));

 

隊列空時remove

BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(1);
//先進先出,清除最先進入的元素
blockingQueue.remove();

 

隊列滿時offer

BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(1);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));

 

隊列空時poll

BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(1);
System.out.println(blockingQueue.poll());

 

隊列滿時put

static BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(1);

創建兩個線程分別爲生產者和消費者

new Thread(new Runnable() {
	@Override
	public void run() {
		try {
                    System.out.println("開始");
			blockingQueue.put("a");
			blockingQueue.put("b");
                    System.out.println("結束");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
},"生產者").start();
		
new Thread(new Runnable() {
	@Override
	public void run() {
		try {
			Thread.sleep(10_000);
			blockingQueue.take();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
},"消費者").start();

上面的例子中,我們可以看到的現象是生產者線程打印”開始“到打印”結束“之間經歷了10秒。

這是因爲當生產者線程向隊列中put一個b元素時,由於隊列已滿,因此發生了阻塞,

此時,進過10秒的休眠的消費者線程從線程中取出a元素,隊列未滿,生產者線程被喚醒,將b放入隊列,程序結束。

 

 

SynchronousQueue隊列


SynchronousQueue沒有容量。

與其他BlockingQueue不同,SynchronousQueue是一個不存儲元素的BlockingQueue。

每一個put操作必須要等待一個take操作,否則不能繼續添加元素,反之亦然。

static BlockingQueue<String> blockingQueue=new SynchronousQueue();
new Thread(new Runnable() {
	@Override
	public void run() {
		try {
			blockingQueue.put("a");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
},"生產者").start();

new Thread(new Runnable() {
	@Override
	public void run() {
		try {
			System.out.println(blockingQueue.take());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
},"消費者").start();

只執行put或者只執行take方法,線程都會處於阻塞狀態,因此put操作必須配合take操作。

 

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