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

使用Semaphore控制对资源的多个副本的并发访问的更多相关文章

  1. 使用Semaphore控制并发访问

    Semaphore,信号量. 用在多线程环境下对共享资源访问的一种协调机制. 当一个线程想要访问共享的资源时,这个线程需要获取Semaphore,如果Semaphore内部计数器的值大于0,Semap ...

  2. Semaphore控制同时访问的线程个数countdownlatch等待多个线程执行完本身线程再执行

    Semaphore控制同时访问的线程个数countdownlatch等待多个线程执行完本身线程再执行 Semaphore控制同时访问的线程个数countdownlatch等待多个线程执行完本身线程再执 ...

  3. JUC并发工具类之Semaphore控制并发线程数

    首先看看关于Semaphore的UML图: 从上图看,信号量的实现原理与锁类似,是基于AQS的:有公平与非公平之分.当初始的资源数为1时就退化为排它锁了,资源总数即state的初始值,在acquire ...

  4. laravel控制器之资源控制器

    资源控制器 Laravel 的资源控制器可以让我们很便捷地构建基于资源的 RESTful 控制器,例如,你可能想要在应用中创建一个控制器,用于处理关于文章存储的 HTTP 请求,使用 Artisan ...

  5. Semaphore可以控制并发访问的线程个数

    public class SemaphoreTest { //信号量,只允许 3个线程同时访问 ); public static void main(String[] args) { Executor ...

  6. Java技术之如何保证同一资源被多个线程并发访问时的完整性?

    常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问.Java语言在多线程编程上实现了完全对象化,提供了对同步机制的良好支持. 在Java中一共有四种方法支持同步,其中前三个是同步 ...

  7. 怎样利用好单片机上的存储器资源来实现OD的存储与访问

    转自:http://www.cnblogs.com/winshton/p/4897789.html 我们知道OD(对象字典)是CANopen的核心,所有功能都是围绕它开展的,是协议栈的数据中心,良好的 ...

  8. view是视图层+action是控制层+service是业务层+dao是数据访问层。

  9. 旧文备份:怎样利用好单片机上的存储器资源来实现OD的存储与访问

    我们知道OD(对象字典)是CANopen的核心,所有功能都是围绕它开展的,是协议栈的数据中心,良好的OD实现是协议栈高效稳定运行的基础,而OD的实现最基本的一点就是怎么去保存它.因为OD的内容比较杂, ...

随机推荐

  1. js操作DOM对象

    js操作DOM对象  (Document Object Model)文档对象模型 nodeType返回值 1:元素节点 2:属性节点 3:文本节点 8:注释节点 9: 文档节点 nodeName 节点 ...

  2. [Swift]LeetCode312. 戳气球 | Burst Balloons

    Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by ...

  3. [Swift]LeetCode486. 预测赢家 | Predict the Winner

    Given an array of scores that are non-negative integers. Player 1 picks one of the numbers from eith ...

  4. ASP.Net Core项目在Mac上使用Entity Framework Core 2.0进行迁移可能会遇到的一个问题.

    在ASP.Net Core 2.0的项目里, 我使用Entity Framework Core 2.0 作为ORM. 有人习惯把数据库的连接字符串写在appSettings.json里面, 有的习惯写 ...

  5. Python必备库

    Python必备库 --default-timeout=100避免网络延迟错误:-U给管理员权限. Python基础库 pip --default-timeout=100 install -U pyg ...

  6. Python操作Redis及连接方式

    前沿:随着互联网的高速发展,数据变得越来越重要,Python成为了人工智能的热门语言,而Nosql数据库已成为日常开发用品. 今天要写的是Python操作Redis Redis的安装我就不介绍了,你可 ...

  7. Python爬虫入门教程 32-100 B站博人传评论数据抓取 scrapy

    1. B站博人传评论数据爬取简介 今天想了半天不知道抓啥,去B站看跳舞的小姐姐,忽然看到了评论,那就抓取一下B站的评论数据,视频动画那么多,也不知道抓取哪个,选了一个博人传跟火影相关的,抓取看看.网址 ...

  8. 在Mac上使用vs-code快速上手c语言学习(入门文,老鸟退散)

    天下事,合久必分.分久必合,你肯定想不到当你逃离到Mac平台这么多年之后,有一天你会再用微软的产品来写代码 :) 其实微软的产品虽然用户体验总是做不到最好,但整体上的确拉低了行业的进入门槛,对于编程也 ...

  9. vs2012 aps.net 4.5尚未在web服务器上注册,您需要手动将Web服务器配置为

    系统换成Windows10安装VS2012打开项目总提示:vs2012 aps.NET 4.5尚未在web服务器上注册,您需要手动将Web服务器配置为使用ASP.net 4.5,这样您的网站才可能正确 ...

  10. 使用Flume消费Kafka数据到HDFS

    1.概述 对于数据的转发,Kafka是一个不错的选择.Kafka能够装载数据到消息队列,然后等待其他业务场景去消费这些数据,Kafka的应用接口API非常的丰富,支持各种存储介质,例如HDFS.HBa ...