【问题标题】:Multithread Binary Semaphore's: Alternating Outputs多线程二进制信号量:交替输出
【发布时间】:2021-08-16 04:01:30
【问题描述】:
  1. 目标是输出由 W、X、y 组成的字符串
    和 z's。
  2. W 和 X 应该交替,并且 W 必须始终在 X 之前。 y 和 z 必须与 y 交替出现在 z 之前。
  3. y 和 z 的总和必须小于输出中任何给定点的 W 数。

到目前为止,我的程序满足前两点,但我在最后一点遇到问题。另外,我对信号量很陌生,想知道我实现的代码是否遵循良好的做法。例如,我最初将二进制信号量的初始值设置为 0,1,2,3,但为了满足第二个条件,将其更改为 0,1,0,1。

public class BinarySemaphore extends Semaphore{

    public BinarySemaphore(int initial) {
        value = (initial>0) ? 1 : 0;
    }

    public synchronized void P() throws InterruptedException {
        while (value==0) {
            wait();
        }
        value = 0;
    }

    public synchronized void V() {
        value = 1;
        notify();
    }
}

public class ProcessW extends App implements Runnable{
    public void run() {
        while (true) {
            try {
                Thread.sleep(1 + (int) (Math.random() * 500));
                bsX.P();
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            System.out.print("W");
            bsW.V();
        }
    }
}
public class ProcessX extends App implements Runnable{
    public void run() {
        while (true) {
            try {
                Thread.sleep(1 + (int) (Math.random() * 500));
                bsW.P();
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            System.out.print("X");
            bsX.V();
        }
    }
}

public class ProcessY extends App implements Runnable{
    public void run() {
        while (true) {
            try {
                Thread.sleep(1 + (int) (Math.random() * 800));
                bsZ.P();
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            System.out.print("y");
            bsY.V();
        }
    }
}

public class ProcessZ extends App implements Runnable{
    public void run() {
        while (true) {
            try {
                Thread.sleep(1 + (int) (Math.random() * 800));
                bsY.P();
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            System.out.print("z");
            bsZ.V();
        }
    }
}

public class App {
    protected static final BinarySemaphore bsW = new BinarySemaphore(
            0);
    protected static final BinarySemaphore bsX = new BinarySemaphore(
            1);

    protected static final BinarySemaphore bsY = new BinarySemaphore(
            0);

    protected static final BinarySemaphore bsZ = new BinarySemaphore(
            1);

    public static void main(String[] args) throws Exception {
        Thread W = new Thread(new ProcessW());
        Thread X = new Thread(new ProcessX());
        Thread Y = new Thread(new ProcessY());
        Thread Z = new Thread(new ProcessZ());
        W.start();
        X.start();
        Y.start();
        Z.start();
        Thread.sleep(3000);
        System.out.println("");
        System.exit(0);
    }
}

这是我的程序当前输出的示例: WXWyzXWXWXyzyWXWXzyzWXyzWXyzWX

【问题讨论】:

    标签: java multithreading semaphore binary-semaphore


    【解决方案1】:
    1. 您的目标没有很好地定义,因为您没有写出实现目标所需的手段。例如,始终打印“WXyzWX”的程序可以满足您的问题。但我假设您特别想使用四个线程,每个线程打印自己的字母,并且您想为此使用信号量。

    2. 信号量用于管理不同线程之间的许多“权限”。一个线程可以semaphore.acquire() 一个权限和semaphore.release() 它在完成它的工作之后。如果在调用acquire() 时没有可用的权限,则线程会等待,直到某个其他线程释放权限。详情请见documentation

      您可以将信号量用于您的目的,但在此之前我必须解释一下“公平” 在多线程方面的含义。默认情况下,信号量(以及所有其他 Java 并发内容)是“不公平”。这意味着当一个权限被释放时,它将被授予任何正在等待的线程,首先考虑整体性能。另一方面,"fair" 信号量总是会给等待时间最长的线程一个新的可用权限。这实际上对线程进行排序,就好像在队列中一样。一般来说,公平结构的工作速度较慢,但​​在我们的例子中,这种公平性非常有用。

      现在谈谈这个想法。您可以按照以下方式考虑您的字母顺序:写X,一个线程需要一个权限,该权限只有在另一个线程写W后才可用,然后写W,您将需要一个权限来自X 线程。所以你可以为这两个线程使用一个信号量,每个线程在打印字母之前和之后从信号量获取和释放一个权限。它的公平性保证了WX 总是交替的(不要忘记默认情况下信号量是不公平的,你必须在其构造函数中指定一个标志以使其公平)。您还应该确保哪个线程首先获得权限,否则您将获得X 始终领先于W

      您可以使用类似的技巧来交替yz,但现在您必须保证您的第三个条件。这也可以使用信号量来实现:要编写yz,您需要一个只能在编写了一些W-s 之后才能获得的权限。我会让你自己思考这个问题。也许一个好主意是随机决定是否释放权限,但这里没有细节:)

      我必须提到,到目前为止,这并不是完成任务的唯一方法,而且信号量可能不是在这里使用的最佳工具。 (但我不认为存在一个特定的最好的。)

    现在在您的代码中添加了一些额外的 cmets:

    1. 您扩展 java Semaphore 的目的到底是什么?你永远不会使用它的任何方法。如果您想使用此代码,您可以删除“扩展”。

    2. 要生成一个从 0 到 N 的随机值,java.util.Random 类中有一个 nextInt(N) 方法。它更适合您的目的。

    3. InterruptedException 是少数可以在大多数情况下安全忽略的问题之一(除非您知道它的含义并想使用它)。我之所以提到它,是因为万一它被抛出,您的输出将与字母和异常混在一起。

    4. 您只需创建一个线程,启动它,然后从不访问它。在这种情况下,您可以将行简化为 new Thread(new ProcessW()).start(),甚至无需创建变量。

    5. P()V() 是可怕的方法名称 - 我几乎无法理解它们应该做什么。

    6. App 类中的 BinarySemaphore 字段为 protected 的目的是什么?你是说private吗?

    7. 您正在通过调用System.exit(0) 来停止所有线程。这样,您就无法区分哪些线程停止和哪些不停止,以及在停止线程后无法执行任何操作。一个简单的解决方案是创建一个对所有线程可见的volatile boolean isRunning = true;(你知道volatile 是什么吗?),将while(true) 替换为while(isRunning),而不是调用System.exit(),只需执行isRunning = false。或者使用中断机制(同样,如果你知道它是什么)。

    【讨论】:

    • OP 正在尝试实现他自己的信号量,而不是使用 Java 的 Semaphore 类。我不清楚他甚至使用 Java 类作为基础。哦,PV 是他们的发明者使用的信号量方法的actual proper names。一个博学的程序员可能想知道这一点。我发现“acquire”是一个非常奇怪的名字,用来代替“P”。
    猜你喜欢
    • 2015-07-12
    • 2020-12-23
    • 2010-10-15
    • 2022-12-22
    • 2012-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-07
    相关资源
    最近更新 更多