View Javadoc
1   package org.slf4j.basicTests;
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 (StateAccessingThread thread : threads) {
118             int val = thread.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 }