阻塞隊列ArrayBlockingQueue說明

1. 什麼是阻塞隊列

對於共享數據,線程A想讀取的隊列中沒有數據時,處於阻塞狀態。直接線程B將數據寫入共享數據,線程被喚醒。


2.參考如下博客的線程狀態轉換圖和狀態說明,自己也做個收藏(來自:https://my.oschina.net/mingdongcheng/blog/139263)


線程間的狀態轉換: 

1. 新建(new):新創建了一個線程對象。

2. 可運行(runnable):線程對象創建後,其他線程(比如main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲取cpu 的使用權 。

3. 運行(running):可運行狀態(runnable)的線程獲得了cpu 時間片(timeslice) ,執行程序代碼。

4. 阻塞(block):阻塞狀態是指線程因爲某種原因放棄了cpu 使用權,也即讓出了cpu timeslice,暫時停止運行。直到線程進入可運行(runnable)狀態,纔有機會再次獲得cpu timeslice 轉到運行(running)狀態。阻塞的情況分三種: 

(一). 等待阻塞:運行(running)的線程執行o.wait()方法,JVM會把該線程放入等待隊列(waitting queue)中。

(二). 同步阻塞:運行(running)的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池(lock pool)中。

(三). 其他阻塞:運行(running)的線程執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入可運行(runnable)狀態。

5. 死亡(dead):線程run()、main() 方法執行結束,或者因異常退出了run()方法,則該線程結束生命週期。死亡的線程不可再次復生。

(來自:https://my.oschina.net/mingdongcheng/blog/139263)


3. 阻塞隊列代碼實踐

1)阻塞隊列不停的有數據寫入,發送線程不停從隊列讀取數據,直到標誌位置爲true。

package com.sean.test;

import java.util.concurrent.ArrayBlockingQueue;

public class SendMessageProcess implements Runnable
{
	private static ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(9);

	public static ArrayBlockingQueue getQueue()
	{
		return queue;
	}

	public static void setQueue(ArrayBlockingQueue queue)
	{
		SendMessageProcess.queue = queue;
	}
	
	public boolean isExit()
	{
		return exit;
	}

	public void setExit(boolean exit) 
	{
		this.exit = exit;
	}

	private boolean exit = false;

	@Override
	public void run() 
	{
		while (!exit)
		{
			System.out.println("begin SendMessageProcess run().and the exit is:" + exit) ;
			String s = null;
			try 
			{
				s = queue.take();
				System.out.println("end SendMessageProcess run()." + s);
			} 
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
	
	public void quit() throws InterruptedException
	{
		queue.put("end the process");
		exit = true;
	}
}

2)主線程往隊列裏寫入數據,新起一個定時任務,過3s時間將標誌位置爲true。爲了保證阻塞線程喚醒啓動,在標誌位置位的時候往隊列新寫入一個字符數據。
否則線程一直處於阻塞狀態,線程掛死。
package com.sean.test;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class SeanTest 
{
	private static ScheduledExecutorService service = null;
	
	public static void main(String[] args) throws InterruptedException 
	{
		SendMessageProcess process = new SendMessageProcess();
		SendMessageProcess.getQueue().put("test1");
		SendMessageProcess.getQueue().put("test2");
		service = Executors.newSingleThreadScheduledExecutor(); 
		service.schedule(new newTask(process), 3, TimeUnit.SECONDS);	
		process.run();
	}
	
	public static class newTask implements Runnable
	{
		SendMessageProcess process = null;
		public newTask(SendMessageProcess process)
		{
			this.process = process;
		}

		@Override
		public void run() 
		{
			System.out.println("Begin newTask run()");
			try 
			{
				process.quit();
			} 
			catch (InterruptedException e) 
			{
				e.printStackTrace();
			}
			System.out.println("End newTask run().exit:" + process.isExit());
			service.shutdown();
		}
	}
}

4. 阻塞隊列在java中適合使用生產者、消費者場景。爲了喚醒消費者,需要生產者往隊列寫入數據。




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