1

I am trying to map some business case to usage of Cyclic Barriers. Let's say we have a promotional offer going on and only 3 customers can get the promotional offer. The rest will not get the offer.

To map this scenario, I have made use of Cyclic Barrier. Even though the code is working, I am not sure how to handle the scenario, where some of the customers, will not be able to get the offer. Right now, I tried to use await() API with a timeout value, so that I can catch TimeoutException and let the customer know, that he could not avail the promotional offer. This led to BarrierBrokenException for another waiting thread.

I would like to know, how can we gracefully handle these scenarios such that the selected customers avail promotional offer, while those who could not follow a different code path.

My code -

public class CyclicBarrierExample {

 public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
    Thread[] threads = new Thread[5];
    CyclicBarrier barrier = new CyclicBarrier(3, ()->{System.out.println("Barrier limit of 3 reached. 3 threads will get the promotional offer!");});
    Runnable nr = new PromotionRunnable(barrier);

    int i = 0;
    for (Thread t : threads) {
        t = new Thread(nr, "Thread " + ++i);
        t.start();
    }
    System.out.println("main thread has completed");
 }

 private static class PromotionRunnable implements Runnable {
    private final CyclicBarrier barrier;

    public PromotionRunnable(final CyclicBarrier barrier) {
        this.barrier = barrier;
    }

    /*
     * As per the doc, BrokenBarrierException is thrown when another thread timed out while the current thread was waiting.
     * This explains why we are able to see both Timeout and Broken Barrier Exceptions.
     */
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " trying to get the promotional offer!");
        try {
            barrier.await(2000L, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
            return;
        } catch (BrokenBarrierException e) {
            System.out.println(Thread.currentThread().getName() + " could not get the promotional offer, due to barrier exception");
            return;
        } catch (TimeoutException e) {
            System.out.println(Thread.currentThread().getName() + " could not get the promotional offer, due to timeout exception");
            return;
        }
        System.out.println(Thread.currentThread().getName() + " got the promotional offer!");
    }
 }
}

Output from one of the runs -

  • Thread 1 trying to get the promotional offer!
  • Thread 4 trying to get the promotional offer!
  • main thread has completed
  • Thread 3 trying to get the promotional offer!
  • Thread 2 trying to get the promotional offer!
  • Thread 5 trying to get the promotional offer!
  • Barrier reached for top 3, they will get the promotional offer!
  • Thread 2 got the promotional offer!
  • Thread 1 got the promotional offer!
  • Thread 5 got the promotional offer!
  • Thread 3 could not get the promotional offer, due to timeout exception
  • Thread 4 could not get the promotional offer, due to barrier exception
V G
  • 18,822
  • 6
  • 51
  • 89
user3842182
  • 371
  • 2
  • 5
  • 8

3 Answers3

1

A CyclicBarrier will only trip when 3 customers are trying to access the offer.

Therefore if only 1 customer is trying to access it, it will block until 2 other customers try to access it too! Once the barrier trips, it is simply reset and the mecanism just starts over. You can observe if create 6+ threads instead of 5.

So CyclicBarrier does not seem to be what you are looking for.

You probably want to count the number of customers who already accessed the offer and refuse it to new customers:

private static class PromotionBarrier {
    private final AtomicBoolean hasAccess = new AtomicBoolean(false);
    private final AtomicLong counter = new AtomicLong(0);
    private final long maxCustomers = 3;
    public boolean hasAccess() {
        if(hasAccess.get()) {
            long value = counter.incrementAndGet();
            if(value <= maxCustomers) {
                return true;
            } else {
                hasAccess.set(false);
                return false;
            }
        }
        return false; 
    }
}

private static class PromotionRunnable implements Runnable {
    private final PromotionBarrier promotionBarrier;

    public PromotionRunnable(final PromotionBarrier promotionBarrier) {
        this.promotionBarrier = barrier;
    }

    @Override
    public void run() {
        if(promotionBarrier.hasAccess()) {
            // Yoohoo I got it!
        } else {
            // Rha I am too late!!
        }
    }
Jean Logeart
  • 52,687
  • 11
  • 83
  • 118
0

You are using a CyclicBarrier incorrectly. It is for a number of threads to synchronise.

A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other. The barrier is called cyclic because it can be re-used after the waiting threads are released.

You probably need to use a Semaphore although I am not sure what you need.

A counting semaphore. Conceptually, a semaphore maintains a set of permits. Each acquire() blocks if necessary until a permit is available, and then takes it. Each release() adds a permit, potentially releasing a blocking acquirer. However, no actual permit objects are used; the Semaphore just keeps a count of the number available and acts accordingly.

You might be looking for an AtomicInteger.

An int value that may be updated atomically. See the java.util.concurrent.atomic package specification for description of the properties of atomic variables. An AtomicInteger is used in applications such as atomically incremented counters, and cannot be used as a replacement for an Integer.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • Yes, after reading through the comments, CyclicBarrier might not be the correct construct to use for my hypothetical scenario. Can you let me know practical scenarios where cyclic barriers are used ? I was also thinking about implementing the same with a counting Semaphore, but was not sure if the use case was valid for a semaphore. – user3842182 Aug 27 '15 at 15:25
  • @user3842182 *CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other.* - Not much more can be said. – OldCurmudgeon Aug 27 '15 at 15:28
0

CyclicBarrier is for when you have several threads, and you want them all to start doing something at the same time. If the barrier is set up for N threads, then it makes the first N-1 threads wait until the N'th thread arrives, and then it lets them all go again.

That probably is not what you want. You want the first 3 threads to claim a prize and the rest to go away empty handed. CyclicBarrier is all about making threads wait for something, but there's nothing that you want your threads to wait for.

Semaphore also is all about making threads wait for something.

I like @OldCurmudgeon's suggestion about using AtomicInteger.

Set an AtomicInteger equal to the number of prizes, and then have each thread call ai.decrementAndGet(). If the result is >= 0, the thread can claim a prize. If the result is < 0, then so sorry, but no prizes are left.

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57