Print integers from 1 to 10 with only 5 Threads in a specific order

  • A+

I have recently started with multi-threading in Java

I have an issue solving a problem where I have got only 5 Threads ranging from T1, T2,...T5.

The task is to print numbers from 1 to 10 in the following order.

T1 -> 1 T2 -> 2 T3 -> 3 T4 -> 4 T5 -> 5 T1 -> 6 T2 -> 7 T3 -> 8 T4 -> 9 T5 -> 10 

I tried solving it with this piece of code.

public static void main(String[] args) throws InterruptedException {     Counter counter = new Counter();     Thread[] tArray = new Thread[] { new Thread(counter, "T1"), new Thread(counter, "T2"),             new Thread(counter, "T3"), new Thread(counter, "T4"), new Thread(counter, "T5") };     for (int i = 0; i < 10; i++) {         if (i < 5) {             tArray[i].start();             tArray[i].join();         } else {             tArray[i - 5] = new Thread(counter, "T" + ((i - 5) + 1)); //Instantiating new Thread which is not allowed.             tArray[i - 5].start();             tArray[i - 5].join();         }     } }  public class Counter implements Runnable {      private int count = 0;      @Override     public synchronized void run() {        System.out.println(Thread.currentThread().getName() + " -> " + ++count);     }  } 

But since only 5 threads are allowed my solution is not accepted since I am also instantiating new Thread in the else block of the for loop.

Any help would be highly appreciated.


Disclaimer: I am answering the practical counterpart of the OP's question — parallel processing with serial input and output. It's much more fun.

Thinking process

  1. I have a serial resource - System.out. No matter how I structure the code there will be explicit or implicit queueing/contention in front of it.
  2. The best way to deal with contention is via explicit queueing (which can be observed, quantified, and addressed, opposed to when implicit queue on a mutex or a synchronized block is used).
  3. My is a 3 step pipeline: ProduceStringizeOutput.
  4. The Stringize step can be done in parallel, providing that the ordered Output can still happen.
  5. I start from a quick & dirty "poor man's" solution. With Java 8 this would be with CompletableFuture-s:

    final Executor inputWorker = newSingleThreadExecutor(); final Executor processingPool = newFixedThreadPool(3); final Executor outputWorker = newSingleThreadExecutor();  final int[] counter = {-1}; // this emulates a non-thread-safe information source CompletableFuture<Void> future = completedFuture(null); for (int i = 0; i < 10; ++i) {     future = future // chaining of futures is essential for serializing subsequent iterations             .thenApplyAsync(unused -> ++counter[0], inputWorker)             .thenApplyAsync(Objects::toString, processingPool)             .thenAcceptAsync(System.out::println, outputWorker); } future.join(); 
  6. Once I have good intuition how it works I may consider industrial techniques like actors, disruptor, or something alike to improve it further.

P.S. - for completeness, one may want to have step #5 slightly differently, first create the whole computation schedule and then trigger it:

final Executor producer = newSingleThreadExecutor(); final Executor stringizer = newFixedThreadPool(3); final Executor printer = newSingleThreadExecutor();  final int[] counter = {-1}; // this emulates a non-thread-safe information source  System.out.println("creating schedule..."); // first schedule the whole amount of work and block the execution on a single "trigger" future final CompletableFuture<Void> trigger = new CompletableFuture<>(); CompletableFuture<Void> future = trigger; for (int i = 0; i < 10; ++i) {     future = future             .thenApplyAsync(unused -> ++counter[0], producer)             .thenApplyAsync(Objects::toString, stringizer)             .thenAcceptAsync(System.out::println, printer); }  // then pull the trigger System.out.println("pulling the trigger..."); trigger.complete(null); future.join(); 


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: