• 使用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
    ", 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
    ", 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

  • 相关阅读:
    kbmmw 5.14.00 发布
    关于C++ auto使用要注意的一点
    git设置socks5代理
    电子书分享网站
    spring cache相关
    intellij idea开启debug热加载/热部署
    git 多次commit合并成一次提交
    es feature一览
    数据中台
    Java Agent
  • 原文地址:https://www.cnblogs.com/mymelody/p/9556037.html
Copyright © 2020-2023  润新知