0

I was practicing this question from oracle blog about happens before, i have a question in how to obtain/access the output of a barrier action in CyclicBarrier.

The blog link

https://blogs.oracle.com/javamagazine/post/quiz-yourself-happens-before-thread-synchronization-in-java-with-cyclicbarrier

the code

 public class CBTest {

    private List<Integer> results = Collections.synchronizedList(new ArrayList<>());
    
    class Calculator extends Thread {
        CyclicBarrier cb;
        int param;

        public Calculator(CyclicBarrier cb, int param) {
            this.cb = cb;
            this.param = param;
        }


        public void run() {
            try {
                results.add(param);
                System.out.println("going to await");
                cb.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

    void doCalculation() {
        CyclicBarrier cb = new CyclicBarrier(2, () -> {
            var ans = results.stream().mapToInt(v -> v.intValue()).sum();
            System.out.println("ANS IS "+ans);
            });
        new Calculator(cb, 2).start();
        new Calculator(cb, 3).start();
    }


    public static void main(String[] args) {
        var test = new CBTest();
        test.doCalculation();
    }
  }

please let me know on how to obtain the value of the cyclic barrier action ans in the main method?

Umar
  • 1,002
  • 3
  • 12
  • 29
  • 1
    That’s the wrong question. Why do you want to make the value available in the main method? Just let the runnable do whatever you want to happen with that value. There is no point in enforcing it to happen in the main method. – Holger Nov 26 '21 at 12:48
  • 1
    Sorry i dont understand, what you mean by wrong question. So if i want to access the output of the barrier action how would i do that. If you can show me an example, that would be helpful as well. – Umar Nov 27 '21 at 00:35
  • 1
    Why does the barrier action have to have an output? Just do, whatever you want to do with that “output” right in the barrier action. It’s also possible that whatever you actually want to do, is better done with a different tool. Your contrived example does not explain why you decided to use a `CyclicBarrier`. Summing two int values does not deserve parallel processing. Merging results of asynchronous computations can be better done with `CompletableFuture`. Depending on the actual computation, a parallel stream might be better. [What is the XY problem?](https://meta.stackexchange.com/a/66378/) – Holger Nov 29 '21 at 07:59
  • As i mentioned in the question, its taken from the oracle blog, so the purpose is not addition, but learning how to obtain an output from barrier action. so it looks like a wrong tool for the problem. – Umar Nov 30 '21 at 16:35

2 Answers2

2

You can make doCalculation return result synchronously by using a additional CountDownLatch(CountDownLatch), in this case, your doCalculation method will be blocked until the calculation result is generated:

    CountDownLatch countDownLatch = new CountDownLatch(1);

    int doCalculation() throws InterruptedException {
        AtomicInteger result = new AtomicInteger();
        CyclicBarrier cb = new CyclicBarrier(2, () -> {
            result.set(results.stream().mapToInt(v -> v.intValue()).sum());
            // count down
            countDownLatch.countDown();
        });
        new Calculator(cb, 2).start();
        new Calculator(cb, 3).start();
        // block util it is countDown.
        countDownLatch.await();
        return result.get();
    }

    public static void main(String[] args) throws InterruptedException {
        var test = new CBTest();
        System.out.println("ANS IS " + test.doCalculation());
    }
zysaaa
  • 1,777
  • 2
  • 8
  • 19
  • It looks like he wants to get the result of the calculation in the main method, so I think an additional ```CountDownLatch``` can be used, just for this method to return synchronously. It has nothing to do with the ```CyclicBarrier ``` that has been used, so I am not suggesting to switch from ```CyclicBarrier``` to ```CountDownLatch``` @Holger – zysaaa Nov 26 '21 at 14:11
  • 1
    Much better. Note that you could also use an `Exchanger` instead of the combination of `AtomicInteger` and `CountDownLatch`. – Holger Nov 26 '21 at 14:38
  • Ok, I will look at this usage, thanks! @Holger – zysaaa Nov 26 '21 at 14:45
1

Since CyclicBarrier takes a Runnable only, it does not return a value. So, you will need to set the result in an externally defined output container - may be an AtomicReference as a field in CBTest. The Runnable can then set the value to it.

Here's a slightly changed version, with my changes marked with the comment CHANGE.

public class CBTest{
    private List<Integer> results = Collections.synchronizedList( new ArrayList<>() );

    /* CHANGE: Value holder. Could be another thread-safe class. */
    private AtomicReference<Integer> answer = new AtomicReference<>( 0 );

    class Calculator extends Thread{
        CyclicBarrier cb;
        int param;

        public Calculator( CyclicBarrier cb, int param ){
            this.cb = cb;
            this.param = param;
        }

        public void run(){
            try{
                results.add( param );
                System.out.println( "going to await" );
                cb.await();
                
                /* CHANGE: Both threads get the same value. */
                System.out.println( answer.get() );
            }
            catch( InterruptedException | BrokenBarrierException e ){
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

    void doCalculation(){
        CyclicBarrier cb = new CyclicBarrier( 2, () -> {
            var ans = results.stream().mapToInt( v -> v.intValue() ).sum();
            System.out.println( "ANS IS " + ans );
            
            /* CHANGE: Set the value here. */
            answer.set( ans );
        } );
        new Calculator( cb, 2 ).start();
        new Calculator( cb, 3 ).start();
    }

    public static void main( String[] args ){
        var test = new CBTest();
        test.doCalculation();
    }
}
Sree Kumar
  • 2,012
  • 12
  • 9