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 c c c c c c c
or maybe
dbac dbac dac dac dc dc c c c c c c c
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
of length 4.Thread
a number (0 to 3) when it starts (using an AtomicInteger
or otherwise).Have each Thread
do something like:
while (barrier.getNumberWaiting() != this.threadNumber) {
}
// Do your adding to the StringBuilder here...
barrier.await();
I.e. each Thread
spins until the number of waiting parties is equal to that Thread
's number.
Whichever is assigned 0 will always go through first, while all the others are stuck spinning. Once that Thread
has done its StringBuilder
thing, it will then await
, which in turn frees the Thread
assigned 1 to go through. The order will stay consistent after the number assignments.
To get the unique id per process, a simple AtomicInteger
can be used.
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;
}
}
};
}