简体   繁体   English

如何使用 CyclingBarrier 同步线程并保留它们的执行顺序?

[英]How to synchronise threads and preserve their execution order with CyclingBarrier?

I want to write a multithread app that prints characters from Strings one by one and after first "round" it would preserve order for the other rounds.我想编写一个多线程应用程序,一个一个地打印字符串中的字符,在第一轮“回合”之后,它将保留其他轮次的顺序。 It should work somehting like this:它应该像这样工作:

For Strings:对于字符串:

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

It would print:它会打印:

abcd abcd acd acd cd cd ccccccc

or maybe或者可能

dbac dbac dac dac dc dc ccccccc

depending on which proccess started first in the very first round取决于在第一轮中首先开始的过程

My solution so far looks like this到目前为止我的解决方案看起来像这样

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();
    }
}

But it doesn't preserve the order of letters written to the console and also i fill the Strings to the some hardcoded value right now, but i would want to make it work properly without equal strings.但它不会保留写入控制台的字母顺序,而且我现在将字符串填充为一些硬编码值,但我想让它在没有相等字符串的情况下正常工作。 Any suggestions on this?对此有何建议?

Since you're asking for a solution with CyclicBarrier , here's a way you could do this with one... It definitely wouldn't be my first thought for how to solve the issue though (assuming the issue isn't 'do this with a CyclicBarrier '...).既然你问了一个解决方案CyclicBarrier ,这里有一个方法,你可以用一个做到这一点......这绝对不会是我对于如何解决这一问题,但(假设这个问题是不是首先想到“这样做有CyclicBarrier '...)。

  • Create a CyclicBarrier of length 4.创建一个长度为 4 的CyclicBarrier
  • Assign each Thread a number (0 to 3) when it starts (using an AtomicInteger or otherwise).在每个Thread开始时为其分配一个编号(0 到 3)(使用AtomicInteger或其他方式)。
  • Have each Thread do something like:让每个Thread执行以下操作:

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

Ie each Thread spins until the number of waiting parties is equal to that Thread 's number.即每个Thread旋转直到等待方的数量等于该Thread的数量。

Whichever is assigned 0 will always go through first, while all the others are stuck spinning.分配为 0 的任何一个将始终首先通过,而所有其他人都卡在旋转中。 Once that Thread has done its StringBuilder thing, it will then await , which in turn frees the Thread assigned 1 to go through.一旦该Thread完成了它的StringBuilder事情,它就会await ,这反过来释放分配为 1 的Thread通过。 The order will stay consistent after the number assignments.编号分配后,顺序将保持一致。


To get the unique id per process, a simple AtomicInteger can be used.要获取每个进程的唯一 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;
            }
        }
    };
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM