    001package org.slf4j;
    002
    003import java.util.concurrent.BrokenBarrierException;
    004import java.util.concurrent.CyclicBarrier;
    005
    006/**
    007 * This class demonstrates that threads accessing the STATE variable always see a consistent value.
    008 *
    009 * During ongoing initialization the observed value is either ONGOING_INITIALIZATION
    010 * or one of {SUCCESS, FAILURE}.
    011 *
    012 * Post initialization the observed value is always one of  {SUCCESS, FAILURE}.
    013 *
    014 * See also http://jira.qos.ch/browse/SLF4J-167
    015 *
    016 * @author ceki
    017 *
    018 */
    019public class DoubleCheckedInt {
    020
    021    final static int THREAD_COUNT = 10 + Runtime.getRuntime().availableProcessors() * 2;
    022    final static int UNINITIALIZED_STATE = 0;
    023    final static int ONGOING_INITIALIZATION = 1;
    024    final static int SUCCESS = 2;
    025    final static int FAILURE = 3;
    026    final static int NUMBER_OF_STATES = FAILURE + 1;
    027
    028    private static int STATE = UNINITIALIZED_STATE;
    029
    030    public static int getState() {
    031        if (STATE == 0) {
    032            synchronized (DoubleCheckedInt.class) {
    033                if (STATE == UNINITIALIZED_STATE) {
    034                    STATE = ONGOING_INITIALIZATION;
    035                    long r = System.nanoTime();
    036                    try {
    037                        Thread.sleep(10);
    038                    } catch (InterruptedException e) {
    039                    }
    040                    if (r % 2 == 0) {
    041                        STATE = SUCCESS;
    042                    } else {
    043                        STATE = FAILURE;
    044                    }
    045                }
    046            }
    047        }
    048        return STATE;
    049    }
    050
    051    static public void main(String[] args) throws InterruptedException, BrokenBarrierException {
    052        StateAccessingThread[] preInitializationThreads = harness();
    053        check(preInitializationThreads, false);
    054
    055        System.out.println("============");
    056        StateAccessingThread[] postInitializationThreads = harness();
    057        check(postInitializationThreads, true);
    058    }
    059
    060    private static StateAccessingThread[] harness() throws InterruptedException, BrokenBarrierException {
    061        StateAccessingThread[] threads = new StateAccessingThread[THREAD_COUNT];
    062        final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
    063        for (int i = 0; i < THREAD_COUNT; i++) {
    064            threads[i] = new StateAccessingThread(barrier);
    065            threads[i].start();
    066        }
    067
    068        barrier.await();
    069        for (int i = 0; i < THREAD_COUNT; i++) {
    070            threads[i].join();
    071        }
    072        return threads;
    073    }
    074
    075    private static void check(StateAccessingThread[] threads, boolean postInit) {
    076
    077        int[] stateCount = getStateCount(threads);
    078        printStateCount(stateCount);
    079
    080        if (stateCount[UNINITIALIZED_STATE] != 0) {
    081            throw new IllegalStateException("getState() should never return a zero value");
    082        }
    083
    084        if (stateCount[SUCCESS] != 0 && stateCount[FAILURE] != 0) {
    085            throw new IllegalStateException("getState() should return consistent values");
    086        }
    087
    088        if (postInit) {
    089            if (stateCount[SUCCESS] != THREAD_COUNT && stateCount[FAILURE] != THREAD_COUNT) {
    090                throw new IllegalStateException("getState() should return consistent values");
    091            }
    092        }
    093
    094    }
    095
    096    private static void printStateCount(int[] stateCount) {
    097        for (int i = 0; i < NUMBER_OF_STATES; i++) {
    098            switch (i) {
    099            case UNINITIALIZED_STATE:
    100                System.out.println("UNINITIALIZED_STATE count: " + stateCount[i]);
    101                break;
    102            case ONGOING_INITIALIZATION:
    103                System.out.println("ONGOING_INITIALIZATION count: " + stateCount[i]);
    104                break;
    105            case SUCCESS:
    106                System.out.println("SUCCESS count: " + stateCount[i]);
    107                break;
    108            case FAILURE:
    109                System.out.println("FAILURE count: " + stateCount[i]);
    110                break;
    111            }
    112        }
    113    }
    114
    115    private static int[] getStateCount(StateAccessingThread[] threads) {
    116        int[] valCount = new int[NUMBER_OF_STATES];
    117        for (int i = 0; i < threads.length; i++) {
    118            int val = threads[i].state;
    119            valCount[val] = valCount[val] + 1;
    120        }
    121        return valCount;
    122    }
    123
    124    static class StateAccessingThread extends Thread {
    125        public int state = -1;
    126        final CyclicBarrier barrier;
    127
    128        StateAccessingThread(CyclicBarrier barrier) {
    129            this.barrier = barrier;
    130        }
    131
    132        public void run() {
    133            try {
    134                barrier.await();
    135            } catch (Exception e) {
    136                e.printStackTrace();
    137            }
    138            state = DoubleCheckedInt.getState();
    139        }
    140    };
    141}