如何使用 CyclingBarrier 同步线程并保留它们的执行顺序?
How to synchronise threads and preserve their execution order with CyclingBarrier?
我想编写一个多线程应用程序,逐个打印字符串中的字符,并且在第一个 "round" 之后它会保留其他轮次的顺序。它应该像这样工作:
对于字符串:
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();
}
}
但它不保留写入控制台的字母顺序,而且我现在将字符串填充为一些硬编码值,但我想让它在没有相等字符串的情况下正常工作。
对此有何建议?
既然你要用 CyclicBarrier
寻求解决方案,这里是 a 你可以用一个来解决这个问题的方法......这绝对不是我的第一个不过考虑了如何解决问题(假设问题不是 'do this with a CyclicBarrier
'...)。
- 创建长度为 4 的
CyclicBarrier
。
- 在开始时(使用
AtomicInteger
或其他方式)为每个 Thread
分配一个 数字 (0 到 3)。
让每个 Thread
做如下事情:
while (barrier.getNumberWaiting() != this.threadNumber) {
}
// Do your adding to the StringBuilder here...
barrier.await();
即每个 Thread
旋转直到等待方的数量等于 Thread
的数量。
分配为 0 的总是先通过,而其他所有的都卡在旋转中。一旦 Thread
完成了它的 StringBuilder
事情,它就会 await
,这反过来又释放了 Thread
分配的 1 通过。编号分配后顺序将保持一致。
要获得每个进程的唯一 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;
}
}
};
}
我想编写一个多线程应用程序,逐个打印字符串中的字符,并且在第一个 "round" 之后它会保留其他轮次的顺序。它应该像这样工作:
对于字符串:
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();
}
}
但它不保留写入控制台的字母顺序,而且我现在将字符串填充为一些硬编码值,但我想让它在没有相等字符串的情况下正常工作。 对此有何建议?
既然你要用 CyclicBarrier
寻求解决方案,这里是 a 你可以用一个来解决这个问题的方法......这绝对不是我的第一个不过考虑了如何解决问题(假设问题不是 'do this with a CyclicBarrier
'...)。
- 创建长度为 4 的
CyclicBarrier
。 - 在开始时(使用
AtomicInteger
或其他方式)为每个Thread
分配一个 数字 (0 到 3)。 让每个
Thread
做如下事情:while (barrier.getNumberWaiting() != this.threadNumber) { } // Do your adding to the StringBuilder here... barrier.await();
即每个 Thread
旋转直到等待方的数量等于 Thread
的数量。
分配为 0 的总是先通过,而其他所有的都卡在旋转中。一旦 Thread
完成了它的 StringBuilder
事情,它就会 await
,这反过来又释放了 Thread
分配的 1 通过。编号分配后顺序将保持一致。
要获得每个进程的唯一 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;
}
}
};
}