在Java使用信号量保护一个资源的多个副本
关于许可证概念的信号量工作。使用一定数量的许可初始化信号量。许可的数量等于可用的共享资源的数量。当一个线程想要访问共享资源时,它获得许可,当访问共享资源完成时,它释放许可。
插图:
Illustration 1: Restaurant application having 10 tables.
餐厅外站着20人,等着进门。在这种情况下,信号量的数量与资源(表)的数量相同,为 10。对于任何客户进入餐厅,他都必须获得许可。获得许可证后,他选择了一张可用的桌子。一旦他的订单完成,他就会释放信号量,使其可供排队等候的其他客户使用。因此,信号量确保一次只有 10 位顾客可以进入餐厅并下订单。
Illustration 2: People waiting in a queue at an ATM center.
如果有 2 台 ATM 机,则一次只有 2 人可以使用 ATM 机并取款。当客户进入 ATM 中心时,他会获得许可(如果有),然后检查哪些 ATM 机可以免费使用。一旦他拿到一台可用的 ATM 机,他就会将其锁上,然后输入 PIN 并取款。取款后,他释放了ATM机上的锁,同时释放了信号量。他必须在取款前锁定 ATM 机的原因是,如果 2 个人在获得信号量后最终在同一台 ATM 机上。这里ATM机是共享资源,信号量的数量等于ATM机的数量。
信号量的工作如下:
对于我们的餐厅应用程序,我们用 10 个许可证初始化我们的信号量,这些许可证等于可用桌子的数量。如果排队等候进入餐厅的顾客有 30 名,顾客 1 获得许可证,进入餐厅。当顾客进入餐厅时,他获得许可证并从 10 张可用桌子中选择 1 张。假设客户 #1 选择表 #5。可用许可证的数量为 9。客户 #2 由于许可证仍然可用而获得另一个许可证并选择表 #7。这样,10 个客户可以获得许可证并选择未占用的表.当客户#11 进入餐厅时,他会被阻止,直到 10 位客户之一释放信号量并离开餐桌。
例子:
Java
// Java Program to Use a Semaphore to Protect more than One
// Copy of a Resource
// Importing input output classes
import java.io.*;
// Importing utility classes
import java.util.Arrays;
import java.util.concurrent.Semaphore;
// Class 1
// Helper class 1
class ATMQueue {
// Member variables
private Semaphore semaphore;
private boolean[] freeAtms;
// Method 1
public ATMQueue()
{
semaphore = new Semaphore(2);
freeAtms = new boolean[2];
Arrays.fill(freeAtms, true);
}
// Method 2
public void withDrawMoney()
{
// Try block to check fo exceptions
try {
// Try to acquire a semaphore.
// If none are available, thread will block here
// till a semaphore becomes available
semaphore.acquire();
// Check for available ATM machine
int atmMachine = getAvailableATM();
// since atm Machine is available to withdraw
// money, we acquire a semaphore
System.out.println(
Thread.currentThread().getName()
+ ":-Withdrawing money from atm number :-"
+ atmMachine);
System.out.println(
"---------------------------------");
Thread.sleep((long)(Math.random() * 1000));
System.out.println(
Thread.currentThread().getName()
+ ":-done withdrawing money");
releaseATM(atmMachine);
System.out.printf(
"ATM machine :- %s is now available",
atmMachine);
System.out.println(
"---------------------------------");
System.out.println(
"About to release the semaphore");
semaphore.release();
System.out.println("Semaphore released");
}
// catch block to handle the exceptions
catch (InterruptedException e) {
// Print the exceptions along with line number
e.printStackTrace();
}
}
// Method 3
private void releaseATM(int atmNumber)
{
// We are making specific atm machine free for use
synchronized (freeAtms)
{
freeAtms[atmNumber] = true;
}
}
// Method 4
private int getAvailableATM()
{
int freeAtm = -1;
// We are using synchronized to ensure that only 1
// thread can access and modify the shared boolean
// array freeAtms
synchronized (freeAtms)
{
for (int i = 0; i < freeAtms.length; i++) {
if (freeAtms[i]) {
freeAtms[i] = false;
freeAtm = i;
break;
}
}
}
return freeAtm;
}
}
// Class 2
// Helper class 2
class WithdrawMoneyTask implements Runnable {
private ATMQueue atmQueue;
public WithdrawMoneyTask(ATMQueue atmQueue)
{
// TODO Auto-generated constructor stub
this.atmQueue = atmQueue;
}
@Override public void run()
{
System.out.println(
Thread.currentThread().getName()
+ ": - about to withdraw money after acquiring the permit");
atmQueue.withDrawMoney();
}
}
// Class 3
// Main class
class GFG {
// Main driver method
public static void main(String[] args)
{
// Print statement
System.out.println("GFG!");
// Creating an object of class 1 in main() method
ATMQueue atmQueue = new ATMQueue();
// Creating Thread class object
Thread thread[] = new Thread[10];
for (int i = 0; i < 10; i++) {
thread[i] = new Thread(
new WithdrawMoneyTask(atmQueue),
"Thread " + i);
}
for (int i = 0; i < 10; i++) {
thread[i].start();
}
}
}
Java
// Java Program to Use a Semaphore to Protect more than One
// Copy of a Resource
// Importing input output classes
import java.io.*;
// Importing utility classes
import java.util.Arrays;
import java.util.concurrent.Semaphore;
// Class 1
// Helper class 1
class ATMQueue {
// Member variables
private Semaphore semaphore;
private boolean[] freeAtms;
// Method 1
public ATMQueue()
{
semaphore = new Semaphore(2);
freeAtms = new boolean[2];
Arrays.fill(freeAtms, true);
}
// Method 2
public void withDrawMoney()
{
// Try block to check fo exceptions
try {
// Try to acquire a semaphore.
// If none are available, thread will block here
// till a semaphore becomes available
semaphore.acquire();
// Check for available ATM machine
int atmMachine = getAvailableATM();
// since atm Machine is available to withdraw
// money, we acquire a semaphore
System.out.println(
Thread.currentThread().getName()
+ ":-Withdrawing money from atm number :-"
+ atmMachine);
System.out.println(
"---------------------------------");
Thread.sleep((long)(Math.random() * 1000));
System.out.println(
Thread.currentThread().getName()
+ ":-done withdrawing money");
releaseATM(atmMachine);
System.out.printf(
"ATM machine :- %s is now available",
atmMachine);
System.out.println(
"---------------------------------");
System.out.println(
"About to release the semaphore");
semaphore.release();
System.out.println("Semaphore released");
}
// catch block to handle the exceptions
catch (InterruptedException e) {
// Print the exceptions along with line number
e.printStackTrace();
}
}
// Method 3
private void releaseATM(int atmNumber)
{
// We are making specific atm machine free for use
synchronized (freeAtms)
{
freeAtms[atmNumber] = true;
}
}
// Method 4
private int getAvailableATM()
{
int freeAtm = -1;
// We are using synchronized to ensure that only 1
// thread can access and modify the shared boolean
// array freeAtms
synchronized (freeAtms)
{
for (int i = 0; i < freeAtms.length; i++) {
if (freeAtms[i]) {
freeAtms[i] = false;
freeAtm = i;
break;
}
}
}
return freeAtm;
}
}
// Class 2
// Helper class 2
class WithdrawMoneyTask implements Runnable {
private ATMQueue atmQueue;
public WithdrawMoneyTask(ATMQueue atmQueue)
{
// TODO Auto-generated constructor stub
this.atmQueue = atmQueue;
}
@Override public void run()
{
System.out.println(
Thread.currentThread().getName()
+ ": - about to withdraw money after acquiring the permit");
atmQueue.withDrawMoney();
}
}
// Class 3
// Main class
class GFG {
// Main driver method
public static void main(String[] args)
{
// Print statement
System.out.println("GFG!");
// Creating an object of class 1 in main() method
ATMQueue atmQueue = new ATMQueue();
// Creating Thread class object
Thread thread[] = new Thread[10];
for (int i = 0; i < 10; i++) {
thread[i] = new Thread(
new WithdrawMoneyTask(atmQueue),
"Thread " + i);
}
for (int i = 0; i < 10; i++) {
thread[i].start();
}
}
}
Java
// Java Program to Use a Semaphore to Protect more than One
// Copy of a Resource
// Importing input output classes
import java.io.*;
// Importing utility classes
import java.util.Arrays;
import java.util.concurrent.Semaphore;
// Class 1
// Helper class 1
class ATMQueue {
// Member variables
private Semaphore semaphore;
private boolean[] freeAtms;
// Method 1
public ATMQueue()
{
semaphore = new Semaphore(2);
freeAtms = new boolean[2];
Arrays.fill(freeAtms, true);
}
// Method 2
public void withDrawMoney()
{
// Try block to check fo exceptions
try {
// Try to acquire a semaphore.
// If none are available, thread will block here
// till a semaphore becomes available
semaphore.acquire();
// Check for available ATM machine
int atmMachine = getAvailableATM();
// since atm Machine is available to withdraw
// money, we acquire a semaphore
System.out.println(
Thread.currentThread().getName()
+ ":-Withdrawing money from atm number :-"
+ atmMachine);
System.out.println(
"---------------------------------");
Thread.sleep((long)(Math.random() * 1000));
System.out.println(
Thread.currentThread().getName()
+ ":-done withdrawing money");
releaseATM(atmMachine);
System.out.printf(
"ATM machine :- %s is now available",
atmMachine);
System.out.println(
"---------------------------------");
System.out.println(
"About to release the semaphore");
semaphore.release();
System.out.println("Semaphore released");
}
// catch block to handle the exceptions
catch (InterruptedException e) {
// Print the exceptions along with line number
e.printStackTrace();
}
}
// Method 3
private void releaseATM(int atmNumber)
{
// We are making specific atm machine free for use
synchronized (freeAtms)
{
freeAtms[atmNumber] = true;
}
}
// Method 4
private int getAvailableATM()
{
int freeAtm = -1;
// We are using synchronized to ensure that only 1
// thread can access and modify the shared boolean
// array freeAtms
synchronized (freeAtms)
{
for (int i = 0; i < freeAtms.length; i++) {
if (freeAtms[i]) {
freeAtms[i] = false;
freeAtm = i;
break;
}
}
}
return freeAtm;
}
}
// Class 2
// Helper class 2
class WithdrawMoneyTask implements Runnable {
private ATMQueue atmQueue;
public WithdrawMoneyTask(ATMQueue atmQueue)
{
// TODO Auto-generated constructor stub
this.atmQueue = atmQueue;
}
@Override public void run()
{
System.out.println(
Thread.currentThread().getName()
+ ": - about to withdraw money after acquiring the permit");
atmQueue.withDrawMoney();
}
}
// Class 3
// Main class
class GFG {
// Main driver method
public static void main(String[] args)
{
// Print statement
System.out.println("GFG!");
// Creating an object of class 1 in main() method
ATMQueue atmQueue = new ATMQueue();
// Creating Thread class object
Thread thread[] = new Thread[10];
for (int i = 0; i < 10; i++) {
thread[i] = new Thread(
new WithdrawMoneyTask(atmQueue),
"Thread " + i);
}
for (int i = 0; i < 10; i++) {
thread[i].start();
}
}
}
GFG!
Thread 7: - about to withdraw money after acquiring the permit
Thread 4: - about to withdraw money after acquiring the permit
Thread 9: - about to withdraw money after acquiring the permit
Thread 2: - about to withdraw money after acquiring the permit
Thread 8: - about to withdraw money after acquiring the permit
Thread 0: - about to withdraw money after acquiring the permit
Thread 1: - about to withdraw money after acquiring the permit
Thread 5: - about to withdraw money after acquiring the permit
Thread 3: - about to withdraw money after acquiring the permit
Thread 6: - about to withdraw money after acquiring the permit
Thread 4:-Withdrawing money from atm number :-1
---------------------------------
Thread 7:-Withdrawing money from atm number :-0
---------------------------------
Thread 7:-done withdrawing money
ATM machine :- 0 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 9:-Withdrawing money from atm number :-0
---------------------------------
Thread 4:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 2:-Withdrawing money from atm number :-1
---------------------------------
Thread 9:-done withdrawing money
ATM machine :- 0 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 8:-Withdrawing money from atm number :-0
---------------------------------
Thread 2:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 0:-Withdrawing money from atm number :-1
---------------------------------
Thread 0:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 1:-Withdrawing money from atm number :-1
---------------------------------
Thread 1:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 5:-Withdrawing money from atm number :-1
---------------------------------
Thread 5:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 3:-Withdrawing money from atm number :-1
---------------------------------
Thread 3:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 6:-Withdrawing money from atm number :-1
---------------------------------
Thread 8:-done withdrawing money
ATM machine :- 0 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 6:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
此外,由于在我们的程序执行中出现导致并发的信号量,因此附加了实时输出。
输出说明:
- 在此代码示例中,有 3 个类。 WithdrawMoneyTask 类定义要执行的作业/任务。所以它实现了 Runnable 接口。
- 它有一个私有成员ATMQueue 代表一台 ATM 机。我们称其为withdrawMoney() 方法。
- 在 ATMQueue 类中,我们有 2 个成员变量。成员变量 semaphore 在构造函数中定义。它被初始化为 2,因为只有 2 台 ATM。成员变量 freeAtms 是一个布尔数组,在构造函数中初始化为 true,表示两个 atm 机器最初都是空闲的。
- 在withdrawMoney() 中,我们首先获取信号量,然后检查哪个atm 机器是空闲的。这是通过 getAvailableATM() 完成的。有可能2个线程获取信号量,进入这块代码修改共享资源变量freeAtms。因此,检查可用 ATM 并将其标记为忙碌的代码位于同步块中。
- 一旦我们找到可用的 ATM 机,我们就取款。一旦取款,我们通过 releaseATM(atmNumber) 方法释放 ATM。此代码也是同步的,以确保只有 1 个线程进入此块并修改 freeAtms 布尔数组。然后我们释放信号量,供其他等待线程根据可用的 ATM 机获取和提取货币。
As per the output, at any given point in time, only 2 threads can acquire the semaphore and withdraw money. If a thread is unable to acquire a semaphore, it is blocked until one of the threads which has acquired the semaphore releases it. This is how semaphores can be used to protect more than one copy of a shared resource which in this case is an ATM machine.