1. 程式人生 > >Java執行緒(十三):BlockingQueue-執行緒的阻塞佇列

Java執行緒(十三):BlockingQueue-執行緒的阻塞佇列

   BlockingQueue作為執行緒容器,可以為執行緒同步提供有力的保障,其主要用到的方法包括:

add(E o); //將指定的元素新增到此佇列中(如果立即可行),在成功時返回 true,其他情況則丟擲 IllegalStateException。
drainTo(Collection<? super E> c);  //移除此佇列中所有可用的元素,並將它們新增到給定 collection 中。
drainTo(Collection<? super E> c,int maxElements);//最多從此佇列中移除給定數量的可用元素,並將這些元素新增到給定 collection 中
offer(E o);  //如果可能的話,將指定元素插入此佇列中。
offer(E o, long timeout, TimeUnit unit);  //將指定的元素插入此佇列中,如果沒有可用空間,將等待指定的等待時間(如果有必要)。
poll(long timeout, TimeUnit unit);  //檢索並移除此佇列的頭部,如果此佇列中沒有任何元素,則等待指定等待的時間(如果有必要)。
put(E o);    //將指定元素新增到此佇列中,如果沒有可用空間,將一直等待(如果有必要)。
remainingCapacity();  //返回在無阻塞的理想情況下(不存在記憶體或資源約束)此佇列能接受的元素數量;如果沒有內部限制,則返回 Integer.MAX_VALUE。
take();  //檢索並移除此佇列的頭部,如果此佇列不存在任何元素,則一直等待。

       上述方法中主要用到的是put()和take()方法,也只有這兩個方法具有阻塞等待功能,另外BlockingQueue 不接受 null 元素。試圖 add、put 或 offer 一個 null 元素時,某些實現會丟擲 NullPointerException。null 被用作指示 poll 操作失敗的警戒值。

       BlockingQueue 可以定義為限定容量的佇列,它有一個 remainingCapacity容量值,超出此容量,便無法無阻塞地 put 額外的元素。也可以定義為沒有容量限制的佇列,沒有容量約束的 BlockingQueue 總是報告 Integer.MAX_VALUE 的剩餘容量。

       BlockingQueue 實現是執行緒安全的。所有排隊方法都可以使用內部鎖定或其他形式的併發控制來自動達到它們的目的。然而,大量的 Collection 操作(addAll、containsAll、retainAll 和 removeAll)沒有 必要自動執行,除非在實現中特別說明。因此,舉例來說,在只添加了 c 中的一些元素後,addAll(c) 有可能失敗(丟擲一個異常)。它實質上不 支援使用任何一種“close”或“shutdown”操作來指示不再新增任何項。這種功能的需求和使用有依賴於實現的傾向。例如,一種常用的策略是:對於生產者,插入特殊的 end-of-stream 或 poison 物件,並根據使用者獲取這些物件的時間來對它們進行解釋。

      BlockingQueue 主要用於實現生產者-使用者佇列,但它另外還支援 Collection 介面。因此,舉例來說,使用 remove(x) 從佇列中移除任意一個元素是有可能的。然而,這種操作通常不 會有效執行,只能有計劃地偶爾使用,比如在取消排隊資訊時。阻塞佇列與Semaphore有很大相似性,但也有很多不同,阻塞佇列一般是一方存資料,另一方釋放資料,而Semaphore通常是同一方獲取和釋放訊號。下面通過一個例子加以說明:

public class BlockingQueueTest {
	public static void main(String[] args) {
		final BlockingQueue queue = new ArrayBlockingQueue(3);
		for(int i=0;i<2;i++){
			new Thread(){
				public void run(){
					while(true){
						try {
						Thread.sleep((long)(Math.random()*1000));
						System.out.println(Thread.currentThread().getName() + "準備放資料!");							
							queue.put(1);
System.out.println(Thread.currentThread().getName() + "已經放了資料," + 	"佇列目前有" + queue.size() + "個數據");
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
				
			}.start();
		}
		new Thread(){
			public void run(){
				while(true){
					try {
					//將此處的睡眠時間分別改為100和1000,觀察執行結果
					Thread.sleep(1000);
					System.out.println(Thread.currentThread().getName() + "準備取資料!");
					queue.take();
					System.out.println(Thread.currentThread().getName() + "已經取走資料," + 
								"佇列目前有" + queue.size() + "個數據");		
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();			
	}
}

       上例中定義了一個最多可以存放3個數據的BlockingQueue,並建立了兩個用於put()的執行緒,一個用於take()的執行緒,這邊能夠更容易使阻塞佇列形成滿佇列,當佇列中的有3個數據的時候,兩個put()執行緒就等待,只有當take()執行緒取走一個數據時才可以繼續往佇列中新增資料。執行結果如下(只去部分結果):
Thread-1準備放資料!
Thread-1已經放了資料,佇列目前有1個數據
Thread-1準備放資料!
Thread-1已經放了資料,佇列目前有2個數據
Thread-0準備放資料!
Thread-0已經放了資料,佇列目前有3個數據
Thread-0準備放資料!
Thread-2準備取資料!
Thread-2已經取走資料,佇列目前有2個數據

      既然阻塞佇列可以實現執行緒之間的等待,那麼我們就可以通過兩個具有1個空間的阻塞佇列可以實現執行緒的同步,關鍵程式碼如下:
BlockingQueue<Integer> queue1 = new ArrayBlockingQueue<Integer>(1);
 BlockingQueue<Integer> queue2 = new ArrayBlockingQueue<Integer>(1);
 public  void sub(int i){
			  	try {
					queue1.put(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				for(int j=1;j<=10;j++){
					System.out.println("sub thread sequece of " + j + ",loop of " + i);
				}
				try {
					queue2.take();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		  }
		  public  void main(int i){
			  	try {
					queue2.put(1);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				for(int j=1;j<=100;j++){
					System.out.println("main thread sequece of " + j + ",loop of " + i);
				}
				try {
					queue1.take();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		  }

        上例中定義了兩個方法,一個sub()和一個main(),兩個方法要實現同步,由於定義的兩個阻塞佇列都是容量為1,所以只要有一個queue1.put(1);那麼sub()方法就必須等待,只有當main()方法中queue1.take();以後sub()方法才可以繼續進行,main()方法也類似。

相關推薦

Java執行(十三)BlockingQueue-執行阻塞佇列

   BlockingQueue作為執行緒容器,可以為執行緒同步提供有力的保障,其主要用到的方法包括: add(E o); //將指定的元素新增到此佇列中(如果立即可行),在成功時返回 true,其他情況則丟擲 IllegalStateException。 drainTo

胡八一之Java(八)執行

多執行緒的優勢:多程序執行需要獨立的記憶體空間,而多執行緒可以共享記憶體,從而提高了執行緒的執行效率。 建立執行緒一般使用兩種方式: 1、繼承Thread類: import java.io.IOException; public class Test extends

Java執行11執行同步操作

什麼情況下需要同步 1、當多執行緒併發,有多段程式碼同時執行時,有時希望某一段程式碼執行的過程中CPU不要切換到其他執行緒工作。這時就需要執行緒同步。 2、如果兩段程式碼是同步的,那麼同一段時間只能執行

Java執行12執行的死鎖

Java執行緒死鎖是一個經典的多執行緒問題,因為不同的執行緒都在等待根本不可能被釋放的鎖,從而導致所有的任務都無法繼續完成。在多執行緒技術中,“死鎖”是必須避免的,因為這會造成執行緒的“假死”。 pac

Java執行24使執行具有有序性

正常的情況下,執行緒的執行時多個執行緒之間執行任務的時機是無序的。可以通過改造程式碼的方式使他們執行具有有序性。 程式碼如下: package unit7; public class Demo10_R

Java執行20執行下的其他元件之CyclicBarrier、Callable、Future和FutureTask

CyclicBarrier         接著講多執行緒下的其他元件,第一個要講的就是CyclicBarrier。CyclicBarrier從字面理解是指迴圈屏障,它可以協同多個執行緒,讓多個執行緒在這個屏障前等待,直到所有執行緒都達到了這個屏障時,再一

Java執行19執行下的其他元件之CountDownLatch、Semaphore、Exchanger

前言       在多執行緒環境下,JDK給開發者提供了許多的元件供使用者使用(主要在java.util.concurrent下),使得使用者不需要再去關心在具體場景下要如何寫出同時兼顧執行緒安全性與高效率的程式碼。之前講過的執行緒池、BlockingQueue都是

Java執行21執行下的其他元件之CyclicBarrier、Callable、Future和FutureTask

public static class CyclicBarrierThread extends Thread { private CyclicBarrier cb; private int sleepSecond; public CyclicBarrie

Java併發程式設計規則構建執行安全的共享物件

構建執行緒安全的共享物件,使其在多執行緒環境下能夠提供安全的訪問。編寫正確的併發程式關鍵在於控制共享、可變的狀態進行訪問管理。synchornized關鍵字既可以阻塞程式,也可以維護操作的原子性,它是一個執行緒安全與非執行緒安全的臨界區標識,通過它我們可以控制物件的記憶體可

Java學習之道執行——讓小球動起來(一)

在講執行緒之前,我們先來談談程序。在我接觸電腦的一段時間後,開始發現當開啟工作管理員後,會有一個程序的顯示,下面的映像名稱裡有很多的可執行檔案。這些可執行檔案有的是系統預設的,有的是我們開啟的,他們都會佔用一定記憶體。 知道了程序,那麼什麼是執行緒呢?執行緒就是執行中程式

Java執行(九)Condition-執行通訊更高效的方式

        接近一週沒更新《Java執行緒》專欄了,主要是這周工作上比較忙,生活上也比較忙,呵呵,進入正題,上一篇講述了併發包下的Lock,Lock可以更好的解決執行緒同步問題,使之更面向物件,並且ReadWriteLock在處理同步時更強大,那麼同樣,執行緒間僅僅互斥是

JAVA併發程式設計(五)建立執行的第三種方式實現Callable介面

眾所周知建立執行緒的方式有兩種:1.繼承Thread類。2.實現Runnable介面。從jdk1.5開始,提供了另一種建立執行緒的方式。今天我們就來看看這第三種方式:實現Callable介面 一、Callable與Runnable 我們直接來看一個使用C

Java併發程式設計(03)執行併發訪問,同步控制

本文原始碼:[GitHub·點這裡](https://github.com/cicadasmile/java-base-parent) || [GitEE·點這裡](https://gitee.com/cicadasmile/java-base-parent) # 一、併發問題 多執行緒學習的時候,要面

作業系統(6)程序---程序概念程序控制、程序狀態、三狀態模型、掛起模型;執行概念使用者執行、核心執行、輕權執行

文章目錄 1:程序相關概念 1. 程序的概念 2. 程序控制塊 3. 程序狀態 4. 三狀態程序模型(就緒、執行、阻塞/等待) 5. 掛起程序模型 2:程序控制

執行(一)建立執行的幾種方法

概括來說就是兩種:1、繼承Thread類,重寫run方法,然後start。不推薦這種,因為java的單繼承特性。 2、Thread類的建構函式中可以接受Runnable任務,所以只要是Runnable例項就可以作為引數給Thread 一般有兩種建立Runnable例項的方法(1)實現Runn

執行執行的問題

1、競態條件         當計算結果的正確性取決於相對時間或者排程器控制的多執行緒交叉時,就會發生競態條件。這句話可能對初次接觸執行緒的讀者來說不太好理解,其實競態條件有兩個相對比較好理解的描述,一個是check-then-act,另外一個是read-modify-wr

Java 進階——多執行優化之執行池 ThreadPoolExecutor的核心容器阻塞佇列詳解(一)

#引言 多執行緒我想無論是後端開發,還是對於App開發者來說都不會陌生,何況Android強制要求不能在主執行緒中做網路請求,於是乎,在很多初學者或者App的原始碼中會出現會多的new Thread…的方式,這樣的程式碼是不優雅而且存在很多的隱患,假如說在使用者

C# 基礎(十四)C#單例模式首先介紹 單執行、多執行、加鎖 單例模式。然後介紹單例模式的執行同步執行有序訪問共享記憶體。

一、簡介 本篇文章將介紹如何使用單例模式,也就是類的例項化,在整個專案的生命週期內,只例項化一次。在單例模式中,往往可以看到如SourceCode.cs:這樣的結構的。 SourceCode.cs: public class Singleton { private static

執行同步解決執行不安全問題

當多個執行緒併發訪問同一個資源物件時,可能會出現執行緒不安全的問題,比如現有50個蘋果,現在有請三個童鞋(小A,小B,小C)上臺表演吃蘋果.因為A,B,C三個人可以同時吃蘋果,此時使用多執行緒技術來實

UI的執行問題執行原因及更新UI的四種方式

1、UI執行緒為什麼設計為單執行緒?  UI控制元件的操作不是執行緒安全的,對於多執行緒併發訪問的時候,如果使用加鎖機制會導致:  UI控制元件的操作變得很複雜。  加鎖的操作必定會導致效率下降。  所以android系統在UI操作上使用單執行緒機制。  2、更新UI有四種方