[译]二进制信号量教程和示例

2019-03-24  本文已影响0人  WHOKNOWME

信号量是一种保护对一个或多个共享资源的访问的计数器。在本教程中,我们将学习如何使用二进制信号量来控制多个线程对共享资源的访问。

信号量如何工作?

您可以将信号量可视化为可以递增或递减的计数器。用一个数字即5来初始化信号量。现在这个信号量可以连续最多递减五次,直到计数器达到0.一旦计数器为零,你可以将它增加到最大值五次使它成为5。信号量的计数器值必须始终在0> = n> = 5的范围内(在我们的例子中)。
显然,信号量不仅仅是计数器。当计数器值为零时,它们能够使线程等待,即它们充当具有计数器功能的锁定。
谈论多线程,当一个线程想要访问一个共享资源(由信号量保护)时,首先,它必须获取信号量。如果信号量的内部计数器大于0,则信号量递减计数器并允许访问共享资源。否则,如果信号量的计数器为0,则信号量将线程置于休眠状态,直到计数器大于0。计数器中的值为0表示其他线程使用所有共享资源,因此想要使用其中一个线程的线程必须等到一个空闲。

当线程完成共享资源的使用时,它必须释放信号量,以便其他线程可以访问共享资源。该操作增加了信号量的内部计数器。

何时使用二进制信号量?

很明显,二进制信号量可以具有0或1的值。这意味着二进制信号量保护对SINGLE共享资源的访问,因此信号量的内部计数器只能取值1或0。

阅读更多:如何在Java中使用锁

如何使用二进制信号量

为了显示二进制信号量的使用,我们将实现一个可由并发任务用于打印其作业的打印队列。此打印队列将受二进制信号量的保护,因此一次只能打印一个线程。

PrintingJob.java
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

此类表示打印机队列/打印机。请注意,我们将值1作为此Semaphore构造函数的参数传递,因此您将创建一个如下二进制信号量打印队列。

class PrinterQueue
{
   private final Semaphore semaphore;
 
   public PrinterQueue()
   {
      semaphore = new Semaphore(1);
   }
 
   public void printJob(Object document)
   {
      try
      {
         semaphore.acquire();
          
         Long duration = (long) (Math.random() * 10000);
         System.out.println(Thread.currentThread().getName() + ": PrintQueue: Printing a Job during " + (duration / 1000) + " seconds :: Time - " + new Date());
         Thread.sleep(duration);
      }
      catch (InterruptedException e)
      {
         e.printStackTrace();
      }
      finally
      {
         System.out.printf("%s: The document has been printed\n", Thread.currentThread().getName());
          
         semaphore.release();
      }
   }
}

我们来测试我们的打印机程序:

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 0: Going to print a document
Thread 9: Going to print a document
Thread 8: Going to print a document
Thread 5: Going to print a document
Thread 7: Going to print a document
Thread 6: Going to print a document
Thread 3: Going to print a document
Thread 4: Going to print a document
Thread 2: Going to print a document
Thread 1: Going to print a document
Thread 0: PrintQueue: Printing a Job during 3 seconds :: Time - Tue Jan 06 18:00:12 IST 2015
Thread 0: The document has been printed
Thread 9: PrintQueue: Printing a Job during 0 seconds :: Time - Tue Jan 06 18:00:16 IST 2015
Thread 9: The document has been printed
Thread 8: PrintQueue: Printing a Job during 7 seconds :: Time - Tue Jan 06 18:00:16 IST 2015
Thread 8: The document has been printed
Thread 5: PrintQueue: Printing a Job during 0 seconds :: Time - Tue Jan 06 18:00:24 IST 2015
Thread 5: The document has been printed
Thread 7: PrintQueue: Printing a Job during 4 seconds :: Time - Tue Jan 06 18:00:24 IST 2015
Thread 7: The document has been printed
Thread 6: PrintQueue: Printing a Job during 3 seconds :: Time - Tue Jan 06 18:00:29 IST 2015
Thread 6: The document has been printed
Thread 3: PrintQueue: Printing a Job during 8 seconds :: Time - Tue Jan 06 18:00:33 IST 2015
Thread 3: The document has been printed
Thread 4: PrintQueue: Printing a Job during 0 seconds :: Time - Tue Jan 06 18:00:41 IST 2015
Thread 4: The document has been printed
Thread 2: PrintQueue: Printing a Job during 4 seconds :: Time - Tue Jan 06 18:00:42 IST 2015
Thread 2: The document has been printed
Thread 1: PrintQueue: Printing a Job during 3 seconds :: Time - Tue Jan 06 18:00:46 IST 2015
Thread 1: The document has been printed

看看printJob()方法。此方法显示了使用信号量实现关键部分时必须遵循的三个步骤,并保护对共享资源的访问:

  1. 首先,使用acquire()方法获取信号量。
  2. 然后,使用共享资源执行必要的操作。
  3. 最后,使用release()方法释放信号量。

信号量类在其构造函数中允许第二个参数。此参数必须采用布尔值。如果你给它传false,你就是在创建一个在非公平模式下工作的信号量。这是默认行为。如果你给它传true,你就是在创建一个能够以公平模式工作的信号量。

原文链接: HowToDoInJava 翻译: iknow4
[ 转载请保留原文出处、译者和译文链接。]

上一篇下一篇

猜你喜欢

热点阅读