package org.slf4j; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * This class demonstrates that threads accessing the STATE variable always see a consistent value. * * During ongoing initialization the observed value is either ONGOING_INITIALIZATION * or one of {SUCCESS, FAILURE}. * * Post initialization the observed value is always one of {SUCCESS, FAILURE}. * * See also http://jira.qos.ch/browse/SLF4J-167 * * @author ceki * */ public class DoubleCheckedInt { final static int THREAD_COUNT = 10 + Runtime.getRuntime().availableProcessors() * 2; final static int UNINITIALIZED_STATE = 0; final static int ONGOING_INITIALIZATION = 1; final static int SUCCESS = 2; final static int FAILURE = 3; final static int NUMBER_OF_STATES = FAILURE + 1; private static int STATE = UNINITIALIZED_STATE; public static int getState() { if (STATE == 0) { synchronized (DoubleCheckedInt.class) { if (STATE == UNINITIALIZED_STATE) { STATE = ONGOING_INITIALIZATION; long r = System.nanoTime(); try { Thread.sleep(10); } catch (InterruptedException e) { } if (r % 2 == 0) { STATE = SUCCESS; } else { STATE = FAILURE; } } } } return STATE; } static public void main(String[] args) throws InterruptedException, BrokenBarrierException { StateAccessingThread[] preInitializationThreads = harness(); check(preInitializationThreads, false); System.out.println("============"); StateAccessingThread[] postInitializationThreads = harness(); check(postInitializationThreads, true); } private static StateAccessingThread[] harness() throws InterruptedException, BrokenBarrierException { StateAccessingThread[] threads = new StateAccessingThread[THREAD_COUNT]; final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1); for (int i = 0; i < THREAD_COUNT; i++) { threads[i] = new StateAccessingThread(barrier); threads[i].start(); } barrier.await(); for (int i = 0; i < THREAD_COUNT; i++) { threads[i].join(); } return threads; } private static void check(StateAccessingThread[] threads, boolean postInit) { int[] stateCount = getStateCount(threads); printStateCount(stateCount); if (stateCount[UNINITIALIZED_STATE] != 0) { throw new IllegalStateException("getState() should never return a zero value"); } if (stateCount[SUCCESS] != 0 && stateCount[FAILURE] != 0) { throw new IllegalStateException("getState() should return consistent values"); } if (postInit) { if (stateCount[SUCCESS] != THREAD_COUNT && stateCount[FAILURE] != THREAD_COUNT) { throw new IllegalStateException("getState() should return consistent values"); } } } private static void printStateCount(int[] stateCount) { for (int i = 0; i < NUMBER_OF_STATES; i++) { switch (i) { case UNINITIALIZED_STATE: System.out.println("UNINITIALIZED_STATE count: " + stateCount[i]); break; case ONGOING_INITIALIZATION: System.out.println("ONGOING_INITIALIZATION count: " + stateCount[i]); break; case SUCCESS: System.out.println("SUCCESS count: " + stateCount[i]); break; case FAILURE: System.out.println("FAILURE count: " + stateCount[i]); break; } } } private static int[] getStateCount(StateAccessingThread[] threads) { int[] valCount = new int[NUMBER_OF_STATES]; for (int i = 0; i < threads.length; i++) { int val = threads[i].state; valCount[val] = valCount[val] + 1; } return valCount; } static class StateAccessingThread extends Thread { public int state = -1; final CyclicBarrier barrier; StateAccessingThread(CyclicBarrier barrier) { this.barrier = barrier; } public void run() { try { barrier.await(); } catch (Exception e) { e.printStackTrace(); } state = DoubleCheckedInt.getState(); } }; }