《java併發程式設計實戰》:執行緒同步輔助類之訊號量(semaphore)
阿新 • • 發佈:2019-02-01
1.訊號量的概念:
訊號量是一種計數器,用來保護一個或者多個共享資源的訪問,它是併發程式設計的一種基礎工具,大多數程式語言都提供了這個機制。
2、訊號量控制執行緒訪問流程:
如果執行緒要訪問一個共享資源,它必須先獲得訊號量。如果訊號量的內部計數器大於0,訊號量將減1,然後允許訪問這個共享資源,計數器大於0意味著有可能使用的資源,因此執行緒將被允許使用其中一個資源,否則,如果訊號量計數器等於0,訊號量將會把執行緒置入休眠直到計數器大於0,計數器等於0的時候意味著所有的共享資源已經被其他執行緒使用了,所以需要訪問這個共享源的執行緒必須等待。
當執行緒使用完某個共享資源時,訊號量必須被釋放,以便於其他執行緒能夠訪問共享資源,釋放操作將使訊號量的內部計數器加1。
練習程式碼:
/** * 實現一個列印佇列,總共有三個印表機,當三個印表機都在工作的時候,其他列印的執行緒任務將阻塞, * 其中一個印表機被釋放後,阻塞的執行緒才能獲取印表機進行列印。 * @author Administrator * */ class PrintQueue { //申明一個訊號量 private final Semaphore semaphore; //boolean陣列,用來記錄印表機狀態 private boolean freeprinters[]; //Lock鎖,當獲取印表機狀態時,進行鎖定,防止多個執行緒使用同一臺印表機 private Lock lockPrinters; public PrintQueue() { semaphore = new Semaphore(3); freeprinters = new boolean[3]; for(int i = 0 ; i < 3;i++){ freeprinters[i] = true; } lockPrinters = new ReentrantLock(); } public void printJob(Object document){ try{ semaphore.acquire();//獲取訊號量,如果訊號量為0,則執行緒阻塞,如果答應0,則訊號量-1 int assignedPrinter = getPrinter(); long duration = (long)(Math.random()*10); System.out.printf("%s:PrintQueue:Printing a Job in Printer %d during %d seconds\r\n" ,Thread.currentThread().getName(),assignedPrinter,duration); TimeUnit.SECONDS.sleep(duration); freeprinters[assignedPrinter] = true; }catch(InterruptedException e){ e.printStackTrace(); }finally { semaphore.release();//訊號量+1 } } private int getPrinter(){ int ret = -1; try{ lockPrinters.lock(); for(int i = 0; i < freeprinters.length;i++){ if(freeprinters[i]){ ret = i; freeprinters[i] = false; break; } } }catch(Exception e){ e.printStackTrace(); }finally{ lockPrinters.unlock(); } return ret; } } class Job implements Runnable{ private PrintQueue printQueue; public Job(PrintQueue printQueue) { this.printQueue = printQueue; } @Override public void run() { System.out.printf("%s :Going to print a job\r\n",Thread.currentThread().getName()); printQueue.printJob(new Object()); System.out.printf("%s:The document has been printed\r\n",Thread.currentThread().getName()); } } public class PrintQueueDemo{ public static void main(String args[]){ PrintQueue printQueue = new PrintQueue(); Thread[] threads = new Thread[10]; for(int i = 0 ; i < 10 ; i++){ threads[i] = new Thread(new Job(printQueue), "Thread"+i); } for(int i = 0 ; i < 10 ; i++){ threads[i].start(); } } }