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. TCP协议学习总结(中)

    很多人都说TCP协议是一个十分复杂的协议,在学习当中,我对协议每一个问题都分解学习后,每一个分解我都能体会和理解它的要点,并不难理解.但我把这些拆分的细节合并后,确认感觉这样一个协议相对“臃肿”但又好 ...

  2. MQTT在react-native中的运行

    mqtt 1.什么是mqtt? MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式 ...

  3. Batch入门教程丨第一章:部署与Hello World!(上)

    Batch入门教程 在本教程中,您将了解并学习与Windows Batch有关的知识和编程方法,用以解决在日常生活中所遇到的简单问题,或者利用Windows Batch建立一个最初级的编程思维方式. ...

  4. Python公众号开发(二)—颜值检测

    上篇文章,我们把自己的程序接入了微信公众号,并且能把用户发送的文本及图片文件原样返回.今天我们把用户的图片通过腾讯的AI平台分析后再返回给用户. 为了防止我的文章被到处转载,贴一下我的公众号[智能制造 ...

  5. FFmpeg 结构体学习(七): AVIOContext 分析

    在上文FFmpeg 结构体学习(六): AVCodecContext 分析我们学习了AVCodec结构体的相关内容.本文,我们将讲述一下AVIOContext. AVIOContext是FFMPEG管 ...

  6. 推荐 | Vue 入门&进阶路线

    今儿跟大家聊聊 Vue . 不得不承认, Vue 越来越受欢迎了.对比 Angular 和 React,虽然三者都是非常优秀的前端框架,但从 GitHub 趋势看,Vue 已经排在第一位,达到了13万 ...

  7. [Swift]LeetCode121. 买卖股票的最佳时机 I | Best Time to Buy and Sell Stock

    Say you have an array for which the ith element is the price of a given stock on day i. If you were ...

  8. [Swift]LeetCode989. 数组形式的整数加法 | Add to Array-Form of Integer

    For a non-negative integer X, the array-form of X is an array of its digits in left to right order.  ...

  9. 面试中程序员常见的Redis"刁难"问题,值得一读!

    导读 在程序员面试过程中Redis相关的知识是常被问到的话题.作为一名在互联网技术行业打击过成百上千名的资深技术面试官,总结了面试过程中经常问到的问题.十分值得一读. Redis有哪些数据结构? 字符 ...

  10. 解决SpringMVC中文乱码问题 -----这是服务器返回参数到前端中文乱码

    方法一,使用(produces = "application/json; charset=utf-8"): @RequestMapping(value="/getUser ...