【问题标题】:How to synchronise threads and preserve their execution order with CyclingBarrier?如何使用 CyclingBarrier 同步线程并保持它们的执行顺序?
【发布时间】:2019-12-08 00:23:44
【问题描述】:

我想编写一个多线程应用程序,逐个打印字符串中的字符,并在第一个“回合”之后保留其他回合的顺序。它应该像这样工作:

对于字符串:

private String[] strings = {"aaaa", "bb", "ccccccccccccc", "dddddd"};

它会打印:

abcd abcd acd acd cd cd c c c c c c c

或许

dbac dbac dac dac dc dc c c c c c c c

取决于在第一轮中哪个进程首先开始

到目前为止我的解决方案是这样的

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class Printer {

    private CyclicBarrier cyclicBarrier;

    private final static String one = "aaa";
    private final static String two = "bbbb";
    private final static String three = "c";
    private final static String four = "dddddd";

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.runSimulation(4);
    }

    private void runSimulation(int numberOfStrings) {
        cyclicBarrier = new CyclicBarrier(numberOfStrings, new AggregatorThread());

        Thread thread = new Thread(new PrintingThread(padSpaces(one, 10)));
        Thread thread1 = new Thread(new PrintingThread(padSpaces(two, 10)));
        Thread thread3 = new Thread(new PrintingThread(padSpaces(three, 10)));
        Thread thread4 = new Thread(new PrintingThread(padSpaces(four, 10)));
        thread.start();
        thread1.start();
        thread3.start();
        thread4.start();
    }

    class AggregatorThread implements Runnable{
        @Override
        public void run() {
            System.out.print("  ");
        }
    }

    class PrintingThread implements Runnable{

        private String toPrint;
        private int iterator;

        public PrintingThread(String toPrint) {
            this.toPrint = toPrint;
            this.iterator = 0;
        }

        @Override
        public void run() {
            while(iterator < toPrint.length()) {
                System.out.print(toPrint.charAt(iterator));
                iterator++;
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private String padSpaces(String inputString, int length) {
        if (inputString.length() >= length) {
            return inputString;
        }
        StringBuilder sb = new StringBuilder();
        while (sb.length() < length - inputString.length()) {
            sb.append(' ');
        }
        StringBuilder sb1 = new StringBuilder(inputString);
        sb1.append(sb);

        return sb1.toString();
    }
}

但它不保留写入控制台的字母顺序,而且我现在将字符串填充为一些硬编码值,但我希望在没有相等字符串的情况下使其正常工作。 对此有何建议?

【问题讨论】:

  • 线程不是这样工作的。这就是有障碍的全部原因。如果您希望它们按顺序执行,则必须按顺序执行它们。
  • 肯定有办法做到这一点,但既然你问的是'with CyclicBarrier',我想你可能误解了CyclicBarrier 的目的:)。

标签: java multithreading runnable synchronized java.util.concurrent


【解决方案1】:

既然您要通过 CyclicBarrier 寻求解决方案,那么您可以使用一个 a 方法...虽然(假设问题不是'用CyclicBarrier'做这个......)。

  • 创建一个长度为 4 的 CyclicBarrier
  • 在每个Thread 启动时分配一个数字(0 到 3)(使用AtomicInteger 或其他方式)。
  • 让每个Thread 执行以下操作:

    while (barrier.getNumberWaiting() != this.threadNumber) {
    }
    
    // Do your adding to the StringBuilder here...
    
    barrier.await();
    

即每个Thread 旋转直到等待方的数量等于Thread 的数量。

分配为 0 的总是先通过,而其他所有的都卡在旋转中。一旦Thread 完成了它的StringBuilder 事情,它就会await,这反过来又释放了分配为1 的Thread。编号分配后顺序将保持一致。


要获取每个进程的唯一 ID,可以使用简单的 AtomicInteger

private final AtomicInteger idCounter = new AtomicInteger();
private final CyclicBarrier barrier = new CyclicBarrier(4);
private final AtomicInteger doneCounter = new AtomicInteger();

public Runnable createRunnable() {
    return () -> {
        final int threadId = this.idCounter.getAndIncrement();

        boolean threadDone = false;
        boolean moreCharacters = true;
        while (true) {
            while (this.barrier.getNumberWaiting() != threadId) {
            }

            // Add to StringBuilder here...

            // Set the 'moreCharacters' flag as false once this thread
            // has handled its String.
            // They will still need to spin though, to make sure the
            // parties waiting keep adding up as appropriate.

            if (!moreCharacters && !threadDone) {

                // 'threadDone' used so that each thread only
                // increments the 'doneCounter' once.

                this.doneCounter.incrementAndGet();
                threadDone = true;
            }

            barrier.await();

            if (this.doneCounter.get() == 4) {
                // Exit out of the loop once all Threads are done.
                break;
            }
        }
    };
}

【讨论】:

  • 感谢您的回答!现在根据你的算法我的程序打印正确的输出说:abcd acbd abc ab a,但是由于我在构造函数中传递了threadNumber,结果总是以相同的顺序打印,所以我怎样才能生成唯一的ID当它调用它的run() 方法时,它的进程是多少?
  • @AdamGłowacki:添加了一个更详尽的示例,它(希望)涵盖了您的问题。正如我上面所说,如果这比按顺序执行它们更好,我会感到惊讶。在不使用CyclicBarrier 的情况下,有更好的方法来对这种事情进行平行化。我还没有在这里处理Exceptions,所以它不会按原样编译。但这是一个草图。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多