A short Java program that uses multiple threads

Viewed 85

I am trying to write a short Java program that uses multiple threads. I want the main-thread to print "i," every 100ms. And the sub-thread prints "Test" every 1000ms.

The result should be like this.

0,1,2,3,4,5,6,7,8,9,Test
10,11,12,13,14,15,16,17,18,19,Test
20,21,22,23,24,25,26,27,28,29,Test
30,31,32,33,34,35,36,37,38,39,Test
40,41,42,43,44,45,46,47,48,49,Test
50,51,52,53,54,55,56,57,58,59,Test
60,61,62,63,64,65,66,67,68,69,Test
70,71,72,73,74,75,76,77,78,79,Test
80,81,82,83,84,85,86,87,88,89,Test
90,91,92,93,94,95,96,97,98,99,Test

But my result is this.

0,1,2,3,4,5,6,7,8,9,Test
10,11,12,13,14,15,16,17,18,Test
19,20,21,22,23,24,25,26,27,Test
28,29,30,31,32,33,34,35,36,Test
37,38,39,40,41,42,43,44,45,Test
46,47,48,49,50,51,52,53,54,Test
55,56,57,58,59,60,61,62,63,Test
64,65,66,67,68,69,70,71,72,Test
73,74,75,76,77,78,79,80,81,Test
82,83,84,85,86,87,88,89,90,91,Test
92,93,94,95,96,97,98,99,

Here is my full code:

public class Main {
    public static void main(String[] args){
        Main.repeatTask(10, 1000);

        //print i, then sleep 100ms
        for(int i=0;i<100;i++){
            System.out.print(i + ",");
            try{
                Thread.sleep(100);
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

    //sleep (time)ms then print "Test"
    public static void repeatTask(int m, int time) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < m; i++) {
                    try {
                        Thread.sleep(time);
                        System.out.println("Test");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

And I can`t change the main method. How can I change my method repeatTask?

1 Answers

The two threads are independent, may as well run into different JVMs, so there is nothing that can guarantee you that the sleep() + waking-up of 1000ms in the other thread will exactly match 10 times the 100ms sleep of the main thread.

If you really can't change the main method, then you can create your own implementation of PrintStream (which is what you use in System.out in order to print):

public static class CustomPrintStream extends PrintStream {

    private final AtomicReference<CountDownLatch> latch;
    private final int repetitions;

    public CustomPrintStream(int repetitions) {
        super(System.out);
        this.repetitions = repetitions;
        this.latch = new AtomicReference<>(new CountDownLatch(repetitions));
    }

    @Override
    public void print(String s) {
        super.print(s);
        latch.get().countDown();
    }

    public void waitForLatch() throws InterruptedException {
        latch.get().await();
    }

    public void resetLatch() {
        latch.set(new CountDownLatch(repetitions));
    }
}

The above implementation uses the same System.out that you have by default in the JVM, except that it:

  1. Contains a number of repetitions (in your case 10)
  2. Contains an atomic reference to a CountDownLatch which is initialized at 10 and re-initialized at 10 every time it touches 0.

Hence, you modify your repeatTask method as follows:

public static void repeatTask(int m, int time) { //<-- note: you don't need time any longer
    CustomPrintStream customPrintStream = new CustomPrintStream(m); //<-- create your custom PrintStream with your number of repetitions
    System.setOut(customPrintStream); //<-- set it as the out for the JVM
    new Thread(() -> {
        for (int i = 0; i < m; i++) {
            try {
                customPrintStream.waitForLatch(); //<-- you wait for the countdown to 10 (the countdown is performed each time the main thread prints a number)
                System.out.println("Test"); //<-- hence you print Test
                customPrintStream.resetLatch(); //<-- and reset the countdown to 10
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
}

Sample output:

0,1,2,3,4,5,6,7,8,9,Test
10,11,12,13,14,15,16,17,18,19,Test
20,21,22,23,24,25,26,27,28,29,Test
30,31,32,33,34,35,36,37,38,39,Test
40,41,42,43,44,45,46,47,48,49,Test
50,51,52,53,54,55,56,57,58,59,Test
60,61,62,63,64,65,66,67,68,69,Test
70,71,72,73,74,75,76,77,78,79,Test
80,81,82,83,84,85,86,87,88,89,Test
90,91,92,93,94,95,96,97,98,99,Test

Note that here I am not waiting 1000 ms, but I am rather waiting that the main thread passes 10 times from the .print() of the System.out. This guarantees you that if something slows down the JVM and so the sum of 10 times is not 1000ms but for example 1200ms, your second thread will still wait to be called 10 times before to print Test.

Disclaimer: I think the above solution is overkill. If it's for a conceptual demonstration then fine, but if it was real production code I would discuss with the owner of main in order to adapt the implementations without having to override the standard PrintStream

Related