DoubleCheckedInt xref

View Javadoc

    1   package org.slf4j;
    2
    3   import java.util.concurrent.BrokenBarrierException;
    4   import java.util.concurrent.CyclicBarrier;
    5
    6   /**
    7    * This class demonstrates that threads accessing the STATE variable always see a consistent value.
    8    *
    9    * During ongoing initialization the observed value is either ONGOING_INITIALIZATION
    10   * or one of {SUCCESS, FAILURE}.
    11   *
    12   * Post initialization the observed value is always one of  {SUCCESS, FAILURE}.
    13   *
    14   * See also http://jira.qos.ch/browse/SLF4J-167
    15   *
    16   * @author ceki
    17   *
    18   */
    19  public class DoubleCheckedInt {
    20
    21      final static int THREAD_COUNT = 10 + Runtime.getRuntime().availableProcessors() * 2;
    22      final static int UNINITIALIZED_STATE = 0;
    23      final static int ONGOING_INITIALIZATION = 1;
    24      final static int SUCCESS = 2;
    25      final static int FAILURE = 3;
    26      final static int NUMBER_OF_STATES = FAILURE + 1;
    27
    28      private static int STATE = UNINITIALIZED_STATE;
    29
    30      public static int getState() {
    31          if (STATE == 0) {
    32              synchronized (DoubleCheckedInt.class) {
    33                  if (STATE == UNINITIALIZED_STATE) {
    34                      STATE = ONGOING_INITIALIZATION;
    35                      long r = System.nanoTime();
    36                      try {
    37                          Thread.sleep(10);
    38                      } catch (InterruptedException e) {
    39                      }
    40                      if (r % 2 == 0) {
    41                          STATE = SUCCESS;
    42                      } else {
    43                          STATE = FAILURE;
    44                      }
    45                  }
    46              }
    47          }
    48          return STATE;
    49      }
    50
    51      static public void main(String[] args) throws InterruptedException, BrokenBarrierException {
    52          StateAccessingThread[] preInitializationThreads = harness();
    53          check(preInitializationThreads, false);
    54
    55          System.out.println("============");
    56          StateAccessingThread[] postInitializationThreads = harness();
    57          check(postInitializationThreads, true);
    58      }
    59
    60      private static StateAccessingThread[] harness() throws InterruptedException, BrokenBarrierException {
    61          StateAccessingThread[] threads = new StateAccessingThread[THREAD_COUNT];
    62          final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
    63          for (int i = 0; i < THREAD_COUNT; i++) {
    64              threads[i] = new StateAccessingThread(barrier);
    65              threads[i].start();
    66          }
    67
    68          barrier.await();
    69          for (int i = 0; i < THREAD_COUNT; i++) {
    70              threads[i].join();
    71          }
    72          return threads;
    73      }
    74
    75      private static void check(StateAccessingThread[] threads, boolean postInit) {
    76
    77          int[] stateCount = getStateCount(threads);
    78          printStateCount(stateCount);
    79
    80          if (stateCount[UNINITIALIZED_STATE] != 0) {
    81              throw new IllegalStateException("getState() should never return a zero value");
    82          }
    83
    84          if (stateCount[SUCCESS] != 0 && stateCount[FAILURE] != 0) {
    85              throw new IllegalStateException("getState() should return consistent values");
    86          }
    87
    88          if (postInit) {
    89              if (stateCount[SUCCESS] != THREAD_COUNT && stateCount[FAILURE] != THREAD_COUNT) {
    90                  throw new IllegalStateException("getState() should return consistent values");
    91              }
    92          }
    93
    94      }
    95
    96      private static void printStateCount(int[] stateCount) {
    97          for (int i = 0; i < NUMBER_OF_STATES; i++) {
    98              switch (i) {
    99              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 }