使用Semaphore控制对资源的多个副本的并发访问
Semaphores 怎样工作?
您可以将信号量看做可以递增或递减的计数器。用一个数字即5来初始化信号量。现在这个信号量可以连续最多递减五次,直到计数器达到0.一旦计数器为零,你可以将它增加到最多五次使其成为5。计数器值信号量必须始终在内部限制0> = n> = 5(在我们的例子中)。
显然,信号量不仅仅是计数器。当计数器值为零时,它们能够使线程等待,即它们充当具有计数器功能的锁。
谈到多线程,当一个线程想要访问一个共享资源(由信号量保护)时,首先,它必须获取信号量。如果信号量的内部计数器大于0,则信号量递减计数器并允许访问共享资源。否则,如果信号量的计数器为0,则信号量将线程置于休眠状态,直到计数器大于0。计数器中的值0表示所有共享资源都被其他线程使用,因此,如果线程想要使用共享资源中的一个,就必须等待到其中一个空闲。
注意:当线程完成共享资源的使用时,它必须释放信号量,以便其他线程可以访问共享资源。 该操作增加信号量的内部计数器。
怎样使用semaphore?
为了演示这个概念,我们将使用信号量来控制可以同时打印多个文档的3台打印机。
PrintingJob.java
此类表示可以提交到打印机队列的独立打印作业。 从队列中,它可以被任何打印机拾取并执行打印作业。 这个类实现了Runnable接口,这样打印机就可以在轮到它时执行它。
class PrintingJob implements Runnable { private PrinterQueue printerQueue; public PrintingJob(PrinterQueue printerQueue) { this.printerQueue = printerQueue; } @Override public void run() { System.out.printf("%s: Going to print a document\n", Thread .currentThread().getName()); printerQueue.printJob(new Object()); } }
PrinterQueue.java
此类表示打印机队列/打印机。 该类有3个主要属性,用于控制从3台打印机中选择一台空闲打印机的逻辑,并将其锁定以打印作业。 打印文档后,将释放打印机,使其再次空闲并可用于从打印队列中打印新作业。
此类有两个方法getPrinter()和releasePrinter(),它们负责获取空闲打印机和将其放回空闲打印机池中。
另一个方法printJob()实际上做核心工作,即获取打印机,执行打印作业,然后释放打印机。
它使用以下两个变量来完成工作:
semaphore:这个变量记录跟踪在任何时间正在使用的打印机数量。
printerLock:用于在从三台可用打印机中检查/获取免费打印机之前锁定打印机池。
class PrinterQueue { //This Semaphore will keep track of no. of printers used at any point of time. private final Semaphore semaphore; //While checking/acquiring a free printer out of three available printers, we will use this lock. private final Lock printerLock; //This array represents the pool of free printers. private boolean freePrinters[]; public PrinterQueue() { semaphore = new Semaphore(3); freePrinters = new boolean[3]; for (int i = 0; i < 3; i++) { freePrinters[i] = true; } printerLock = new ReentrantLock(); } public void printJob(Object document) { try { //Decrease the semaphore counter to mark a printer busy semaphore.acquire(); //Get the free printer int assignedPrinter = getPrinter(); //Print the job Long duration = (long) (Math.random() * 10000); System.out.println(Thread.currentThread().getName() + ": Printer " + assignedPrinter + " : Printing a Job during " + (duration / 1000) + " seconds :: Time - " + new Date()); Thread.sleep(duration); //Printing is done; Free the printer to be used by other threads. releasePrinter(assignedPrinter); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.printf("%s: The document has been printed\n", Thread .currentThread().getName()); //Increase the semaphore counter back semaphore.release(); } } //Acquire a free printer for printing a job private int getPrinter() { int foundPrinter = -1; try { //Get a lock here so that only one thread can go beyond this at a time printerLock.lock(); //Check which printer is free for (int i = 0; i < freePrinters.length; i++) { //If free printer found then mark it busy if (freePrinters[i]) { foundPrinter = i; freePrinters[i] = false; break; } } } catch (Exception e) { e.printStackTrace(); } finally { //Allow other threads to check for free priniter printerLock.unlock(); } return foundPrinter; } //Release the printer private void releasePrinter(int i) { printerLock.lock(); //Mark the printer free freePrinters[i] = true; printerLock.unlock(); } }
我们来测试打印机程序:
public class SemaphoreExample { public static void main(String[] args) { PrinterQueue printerQueue = new PrinterQueue(); Thread thread[] = new Thread[10]; for (int i = 0; i < 10; i++) { thread[i] = new Thread(new PrintingJob(printerQueue), "Thread " + i); } for (int i = 0; i < 10; i++) { thread[i].start(); } } } Output: Thread 1: Going to print a document Thread 4: Going to print a document Thread 9: Going to print a document Thread 8: Going to print a document Thread 6: Going to print a document Thread 7: Going to print a document Thread 2: Going to print a document Thread 5: Going to print a document Thread 3: Going to print a document Thread 0: Going to print a document Thread 9: PrintQueue 2 : Printing a Job during 2 seconds :: Time - Tue Jan 13 16:28:58 IST 2015 Thread 4: PrintQueue 1 : Printing a Job during 7 seconds :: Time - Tue Jan 13 16:28:58 IST 2015 Thread 1: PrintQueue 0 : Printing a Job during 1 seconds :: Time - Tue Jan 13 16:28:58 IST 2015 Thread 1: The document has been printed Thread 8: PrintQueue 0 : Printing a Job during 1 seconds :: Time - Tue Jan 13 16:29:00 IST 2015 Thread 9: The document has been printed Thread 6: PrintQueue 2 : Printing a Job during 0 seconds :: Time - Tue Jan 13 16:29:01 IST 2015 Thread 6: The document has been printed Thread 7: PrintQueue 2 : Printing a Job during 4 seconds :: Time - Tue Jan 13 16:29:01 IST 2015 Thread 8: The document has been printed Thread 2: PrintQueue 0 : Printing a Job during 5 seconds :: Time - Tue Jan 13 16:29:02 IST 2015 Thread 7: The document has been printed Thread 5: PrintQueue 2 : Printing a Job during 8 seconds :: Time - Tue Jan 13 16:29:05 IST 2015 Thread 4: The document has been printed Thread 3: PrintQueue 1 : Printing a Job during 4 seconds :: Time - Tue Jan 13 16:29:06 IST 2015 Thread 2: The document has been printed Thread 0: PrintQueue 0 : Printing a Job during 4 seconds :: Time - Tue Jan 13 16:29:08 IST 2015 Thread 3: The document has been printed Thread 0: The document has been printed Thread 5: The document has been printed
在上面的示例中,使用3作为构造函数的参数创建信号量对象。 调用acquire()方法的前三个线程将获得对打印机的访问权限,而其余线程将被阻塞。 当一个线程完成关键部分并释放信号量时,另一个线程将获取它。
在printJob()方法中,线程获取分配用于打印此作业的打印机的索引。
这就是这个简单但重要的概念。 如果有的话,请把你的问题和评论留下。
原文链接: howtodoinjava 翻译: 随波不逐流- wavemelody
译文链接: https://www.cnblogs.com/mymelody/p/9556037.html