📜  在Java使用信号量保护一个资源的多个副本

📅  最后修改于: 2022-05-13 01:55:47.666000             🧑  作者: Mango

在Java使用信号量保护一个资源的多个副本

关于许可证概念的信号量工作。使用一定数量的许可初始化信号量。许可的数量等于可用的共享资源的数量。当一个线程想要访问共享资源时,它获得许可,当访问共享资源完成时,它释放许可。

插图:

餐厅外站着20人,等着进门。在这种情况下,信号量的数量与资源(表)的数量相同,为 10。对于任何客户进入餐厅,他都必须获得许可。获得许可证后,他选择了一张可用的桌子。一旦他的订单完成,他就会释放信号量,使其可供排队等候的其他客户使用。因此,信号量确保一次只有 10 位顾客可以进入餐厅并下订单。



如果有 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 机获取和提取货币。