在Java中使用Semaphores

时间:2015-10-26 02:17:23

标签: java multithreading semaphore thread-synchronization

我感到内疚地向StackOverflow求助于学校,但我已经耗尽了我的资源,无法为我的生活找到这个。对于我的一个类,我需要了解如何在Java中构造和正确使用Semaphores。其中一个练习有以下代码:

import java.lang.Thread;
import java.util.concurrent.*;

public class ThreadSync
{
    private static boolean runFlag = true;

    public static void main( String[] args ) {
        Runnable[] tasks = new Runnable[37];
        Thread[] threads = new Thread[37];
        // create 10 digit threads
        for (int d=0; d<10; d++) {
            tasks[d] = new PrintDigit(d);
            threads[d] = new Thread( tasks[d] );
            threads[d].start();
        }
        // create 26 letter threads
        for (int d=0; d<26; d++) {
            tasks[d+10] = new PrintLetter((char)('A'+d));
            threads[d+10] = new Thread( tasks[d+10] );
            threads[d+10].start();
        }
        // create a coordinator thread
        tasks[36] = new PrintSlashes();
        threads[36] = new Thread( tasks[36] );
        threads[36].start();

        // Let the threads to run for a period of time
        try {
            Thread.sleep(50);
        }
        catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        runFlag = false;

        // Interrupt the threads
        for (int i=0; i<37; i++) threads[i].interrupt();
    }

    public static class PrintDigit implements Runnable 
    {
        int digit;
        public PrintDigit(int d) { digit=d; }
        public void run(){
            while (runFlag) {
                System.out.printf( "%d\n", digit);
            }
        }
    }
    public static class PrintLetter implements Runnable 
    {
        char letter;
        public PrintLetter(char c) { letter=c; }
        public void run(){
            while (runFlag) {
                System.out.printf( "%c\n", letter);
            }
         }
    }
    public static class PrintSlashes implements Runnable 
    {
        public void run(){
            while (runFlag) {
                System.out.printf( "%c\n", '/');
                System.out.printf( "%c\n", '\\');
            }
        }
    }
}

我需要修改代码,只需添加“信号量相关的语句”,这样程序就会重复打印出一个'/'后跟三个数字,然后是'\'后跟两个字母。

他们给出的例子如下:

  /156\BA/376\YZ/654\JK/257\HG/445\DD…

非常感谢任何帮助。我通常很擅长自学,但这些线程让我头晕目眩!谢谢!

1 个答案:

答案 0 :(得分:3)

我质疑这位教师的教学方法和编码实践,但我会回答这个问题。事实上,我认为这个问题过于复杂,这让我更愿意回答这个问题,而不是让你自己解决。

该示例有点违反直觉,因为线程不是用于正常目的,即允许并发执行,而只是作为理解信号量的练习。因此,信号量也必须以某种非标准的方式使用,作为线程之间的信号,而不是它们正常​​使用管理资源。您将了解信号量在这种人为的情况下如何工作,但最终可能无法理解它们在正常情况下的使用方式。但是,可以通过阅读Semaphore类中的Javadoc来解决这个问题,所以回到教师的设计案例中。

很明显,运行PrintSlashes.run()的线程旨在充当管理器,确定何时运行数字线程以及何时运行字符线程。它需要告诉数字线程何时可以运行,并且需要告诉字符线程何时可以运行。此外,它需要知道何时打印了三个数字,并且需要知道何时打印了两个字符。这是需要转移的四条信息,最简单的模型是使用四个信号量对象。

信号量对象应代表以下四件事:

  • 可以打印的数字
  • 已打印的数字(在最近的正斜杠之后)
  • 可打印的字符
  • 已打印的字符(在最近的反斜杠之后)

PrintDigit.run()应该在打印每个数字之前从数字可用信号量中获取许可证;允许数字可用信号量将数字打印限制为一次三个。打印数字后,该方法应从数字打印信号量中释放许可证 - 注意,而不是数字可用信号量 - 以指示已打印数字。我确定你可以弄清楚PrintLetter.run()应该做什么。顺便说一句,线程获取一个信号量但释放不同信号量的事实是这个例子的设计方式之一;通常线程释放它们获得的相同信号量。

PrintSlashes.run()应该在打印斜杠后从数字可用信号量中释放三个许可,然后在打印反斜杠之前从打印的数字信号中获取三个许可。释放三位数字允许PrintDigit线程打印三位数字,等待获取三位数字确保打印三位数字然后再继续。同样,您应该能够弄清楚打印反斜杠后会发生什么。

请注意,数字信号量对象应该使用0个许可进行初始化,这样数字线程就会等待斜线线程启动。

另外两个警告:

  1. 要使代码在示例输出显示时起作用,您还需要从每个打印的字符串中删除\n,否则每个字符将位于不同的行上。但是,教师可能希望每个字符在不同的行上,并且给出了不良的样本输出。你必须猜测他真正想要的是什么。

  2. 如果您想使代码具有防弹功能,则可能需要在System.out上进行同步,如下所述:

    Synchronization and System.out.println

    但是,您的导师可能并不关心此练习的问题。

  3. 最后,应该进行以下更正以修复代码中的其他不良做法:

    • 不应使用通配符导入,因为您并未使用并发包中的大量类。在我看来,永远不应该使用通配符导入。通配符导入会因为很难看到类的来源而损害代码的易读性,而易读性是代码中最重要的一个方面。

    • 有意义的常数,例如&#34; 10&#34;,&#34; 26&#34;,&#34; 36&#34;和&#34; 37&#34;在这段代码中,不应该写成文字,而应该使用定义的常量,例如static final int NUMBER_OF_DIGITS = 10;。然后代码本身可以使用符号NUMBER_OF_DIGITS,使其更易读,也更易于维护,因为您可以轻松更改常量的值 - 例如,如果要将代码转换为八进制,则为8 - 担心你会遗漏一些常数。

    • 有意义的常量在具有逻辑关系时尤其不应写为文字。在这种情况下,即使static final int NUMBER_OF_CHARACTERS = 36也不是好习惯;它应该是static final int NUMBER_OF_CHARACTERS = NUMBER_OF_DIGITS + NUMBER_OF_LETTERS;,使逻辑和数字关系清晰。

    你是否真的想在你上班的工作中做出这些修正,取决于你认为教师的反应会得到很好的修正。