001package org.slf4j.basicTests; 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 (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}