使用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
使用Semaphore控制对资源的多个副本的并发访问的更多相关文章
- 使用Semaphore控制并发访问
Semaphore,信号量. 用在多线程环境下对共享资源访问的一种协调机制. 当一个线程想要访问共享的资源时,这个线程需要获取Semaphore,如果Semaphore内部计数器的值大于0,Semap ...
- Semaphore控制同时访问的线程个数countdownlatch等待多个线程执行完本身线程再执行
Semaphore控制同时访问的线程个数countdownlatch等待多个线程执行完本身线程再执行 Semaphore控制同时访问的线程个数countdownlatch等待多个线程执行完本身线程再执 ...
- JUC并发工具类之Semaphore控制并发线程数
首先看看关于Semaphore的UML图: 从上图看,信号量的实现原理与锁类似,是基于AQS的:有公平与非公平之分.当初始的资源数为1时就退化为排它锁了,资源总数即state的初始值,在acquire ...
- laravel控制器之资源控制器
资源控制器 Laravel 的资源控制器可以让我们很便捷地构建基于资源的 RESTful 控制器,例如,你可能想要在应用中创建一个控制器,用于处理关于文章存储的 HTTP 请求,使用 Artisan ...
- Semaphore可以控制并发访问的线程个数
public class SemaphoreTest { //信号量,只允许 3个线程同时访问 ); public static void main(String[] args) { Executor ...
- Java技术之如何保证同一资源被多个线程并发访问时的完整性?
常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问.Java语言在多线程编程上实现了完全对象化,提供了对同步机制的良好支持. 在Java中一共有四种方法支持同步,其中前三个是同步 ...
- 怎样利用好单片机上的存储器资源来实现OD的存储与访问
转自:http://www.cnblogs.com/winshton/p/4897789.html 我们知道OD(对象字典)是CANopen的核心,所有功能都是围绕它开展的,是协议栈的数据中心,良好的 ...
- view是视图层+action是控制层+service是业务层+dao是数据访问层。
- 旧文备份:怎样利用好单片机上的存储器资源来实现OD的存储与访问
我们知道OD(对象字典)是CANopen的核心,所有功能都是围绕它开展的,是协议栈的数据中心,良好的OD实现是协议栈高效稳定运行的基础,而OD的实现最基本的一点就是怎么去保存它.因为OD的内容比较杂, ...
随机推荐
- C语言面试题分类->回调
本文主要讲解如果实现回调,特别是在封装接口的时候,回调显得特别重要,我们首先假设有两个程序员在写代码,A程序员写底层驱动接口,B程序员写上层应用程序,然而此时底层驱动接口A有一个数据d需要传输给B,此 ...
- NeuChar 平台使用及开发教程(五):使用 NeuChar 的关键字回复服务
在上一篇<NeuChar 平台使用及开发教程(四):使用 NeuChar 的素材服务>中,我们已经完成了素材的添加,下面,让我们来设置一个关键字回复,并同步到应设置好Neural Endi ...
- 【从零开始搭建自己的.NET Core Api框架】(六)泛型仓储的作用
系列目录 一. 创建项目并集成swagger 1.1 创建 1.2 完善 二. 搭建项目整体架构 三. 集成轻量级ORM框架——SqlSugar 3.1 搭建环境 3.2 实战篇:利用SqlSuga ...
- [Swift]LeetCode74. 搜索二维矩阵 | Search a 2D Matrix
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the follo ...
- [Swift]LeetCode783. 二叉搜索树结点最小距离 | Minimum Distance Between BST Nodes
Given a Binary Search Tree (BST) with the root node root, return the minimum difference between the ...
- 机器学习入门 - Google机器学习速成课程 - 笔记汇总
机器学习入门 - Google机器学习速成课程 https://www.cnblogs.com/anliven/p/6107783.html MLCC简介 前提条件和准备工作 完成课程的下一步 机器学 ...
- VSphere虚拟化ESXi的安装及基本管理
虚拟化和云计算技术正在快速的发展,新的概念.观点.产品不断涌现.服务器虚拟化技术受到了人们的高度重视,普遍相信虚拟化将成为数据中心的重要组成部分.vSphere是VMware公司推出的一套服务器虚拟化 ...
- MySQL面试必考知识点:揭秘亿级高并发数据库调优与最佳实践法则
做业务,要懂基本的SQL语句: 做性能优化,要懂索引,懂引擎: 做分库分表,要懂主从,懂读写分离... 数据库的使用,是开发人员的基本功,对它掌握越清晰越深入,你能做的事情就越多. 今天我们用10分钟 ...
- Python内置函数(3)——any
英文文档: any(iterable) Return True if any element of the iterable is true. If the iterable is empty, re ...
- PyCharm证书过期:Your license has expired
报错“your evaluation license has expired, pycharm will now exit”1.解决步骤,点击‘Activation code’,授权激活pycharm ...