Repository: pinpoint-apm/pinpoint
Issue: Improvement thread safety of InterceptorRegistry
Labels: bug
enhancement

improvement thread safety of `InterceptorRegistry`

`array[index]=value`  is not thread safe.


Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/bootstrap-core/pom.xml b/bootstrap-core/pom.xml
index 1d3b70ebedc2..7f53514ed877 100644
--- a/bootstrap-core/pom.xml
+++ b/bootstrap-core/pom.xml
@@ -13,6 +13,26 @@
     <packaging>jar</packaging>
 
     <dependencies>
+        <!-- Logging depedencies -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!-- commons-logging-adapter -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+        </dependency>
     </dependencies>
 
 
diff --git a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/InterceptorRegistry.java b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/InterceptorRegistry.java
index 867294d3d035..76c98a400f2c 100644
--- a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/InterceptorRegistry.java
+++ b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/InterceptorRegistry.java
@@ -23,18 +23,19 @@
 /**
  * @author emeroad
  */
-public class InterceptorRegistry {
+public final class InterceptorRegistry {
 
-    private static final LoggingInterceptor DUMMY = new LoggingInterceptor("com.navercorp.pinpoint.profiler.interceptor.DUMMY");
+    private static final LoggingInterceptor LOGGING_INTERCEPTOR = new LoggingInterceptor("com.navercorp.pinpoint.profiler.interceptor.LOGGING_INTERCEPTOR");
 
     public static final InterceptorRegistry REGISTRY = new InterceptorRegistry();
 
     private final static int DEFAULT_MAX = 4096;
-    private final int max;
+    private final int registrySize;
 
     private final AtomicInteger id = new AtomicInteger(0);
-    private final StaticAroundInterceptor[] index;
-    private final SimpleAroundInterceptor[] simpleIndex;
+
+    private final WeakAtomicReferenceArray<StaticAroundInterceptor> staticIndex;
+    private final WeakAtomicReferenceArray<SimpleAroundInterceptor> simpleIndex;
 
 //    private final ConcurrentMap<String, Integer> nameIndex = new ConcurrentHashMap<String, Integer>();
 
@@ -42,24 +43,29 @@ public InterceptorRegistry() {
         this(DEFAULT_MAX);
     }
 
-    InterceptorRegistry(int max) {
-        this.max = max;
-        this.index = new StaticAroundInterceptor[max];
-        this.simpleIndex = new SimpleAroundInterceptor[max];
+    InterceptorRegistry(int maxRegistrySize) {
+        if (maxRegistrySize < 0) {
+            throw new IllegalArgumentException("negative maxRegistrySize:" + maxRegistrySize);
+        }
+        this.registrySize = maxRegistrySize;
+        this.staticIndex = new WeakAtomicReferenceArray<StaticAroundInterceptor>(maxRegistrySize, StaticAroundInterceptor.class);
+        this.simpleIndex = new WeakAtomicReferenceArray<SimpleAroundInterceptor>(maxRegistrySize, SimpleAroundInterceptor.class);
     }
 
 
-    public int addInterceptor0(StaticAroundInterceptor interceptor) {
+    public int addStaticInterceptor(StaticAroundInterceptor interceptor) {
         if (interceptor == null) {
             return -1;
         }
+        return addInterceptor(interceptor, staticIndex);
+    }
+
+    private <T extends Interceptor> int addInterceptor(T interceptor, WeakAtomicReferenceArray<T> index) {
         final int newId = nextId();
-        if (newId >= max) {
-            throw new IndexOutOfBoundsException("size=" + index.length + " id=" + id);
+        if (newId >= registrySize) {
+            throw new IndexOutOfBoundsException("size=" + index.length() + " id=" + id);
         }
-
-        this.index[newId] = interceptor;
-//        this.nameIndex.put(interceptor.getClass().getName(), newId);
+        index.set(newId, interceptor);
         return newId;
     }
 
@@ -72,72 +78,59 @@ int addSimpleInterceptor0(SimpleAroundInterceptor interceptor) {
             return -1;
         }
         final int newId = nextId();
-        if (newId >= max) {
-            throw new IndexOutOfBoundsException("size=" + index.length + " id=" + id);
+        if (newId >= registrySize) {
+            throw new IndexOutOfBoundsException("size=" + staticIndex.length() + " id=" + id);
         }
 
-        this.simpleIndex[newId] = interceptor;
-//        this.nameIndex.put(interceptor.getClass().getName(), newId);
+        this.simpleIndex.set(newId, interceptor);
         return newId;
     }
 
     public StaticAroundInterceptor getInterceptor0(int key) {
-        StaticAroundInterceptor interceptor = index[key];
+        final StaticAroundInterceptor interceptor = staticIndex.get(key);
         if (interceptor == null) {
-            // return DUMMY upon wrong logic
-            return DUMMY;
+            // return LOGGING_INTERCEPTOR upon wrong logic
+            return LOGGING_INTERCEPTOR;
         }
         return interceptor;
     }
 
+    public Interceptor findInterceptor0(int key) {
+        final SimpleAroundInterceptor simpleInterceptor = this.simpleIndex.get(key);
+        if (simpleInterceptor != null) {
+            return simpleInterceptor;
+        }
+        final StaticAroundInterceptor staticAroundInterceptor = this.staticIndex.get(key);
+        if (staticAroundInterceptor != null) {
+            return staticAroundInterceptor;
+        }
+        Logger logger = Logger.getLogger(InterceptorRegistry.class.getName());
+        if (logger.isLoggable(Level.WARNING)) {
+            logger.warning("interceptor not found. id:" + key);
+        }
+        return LOGGING_INTERCEPTOR;
+    }
+
     SimpleAroundInterceptor getSimpleInterceptor0(int key) {
-        SimpleAroundInterceptor interceptor = simpleIndex[key];
+        final SimpleAroundInterceptor interceptor = simpleIndex.get(key);
         if (interceptor == null) {
-            // return DUMMY upon wrong logic
-            return DUMMY;
+            // return LOGGING_INTERCEPTOR upon wrong logic
+            return LOGGING_INTERCEPTOR;
         }
         return interceptor;
     }
 
-//    SimpleAroundInterceptor getInterceptor0(int key) {
-//        StaticAfterInterceptor interceptor = index[key];
-//        if (interceptor == null) {
-//            // return DUMMY upon wrong logic
-//            return DUMMY;
-//        }
-//        return interceptor;
-//    }
-//    public Interceptor findInterceptor0(String interceptorName) {
-//        Integer indexNumber = this.nameIndex.get(interceptorName);
-//        if (indexNumber != null) {
-//            return index[indexNumber];
-//        }
-//        return null;
-//    }
 
     public static int addInterceptor(StaticAroundInterceptor interceptor) {
-        return REGISTRY.addInterceptor0(interceptor);
+        return REGISTRY.addStaticInterceptor(interceptor);
     }
 
     public static StaticAroundInterceptor getInterceptor(int key) {
         return REGISTRY.getInterceptor0(key);
     }
 
-
     public static Interceptor findInterceptor(int key) {
-        SimpleAroundInterceptor simpleInterceptor = REGISTRY.getSimpleInterceptor0(key);
-        if (simpleInterceptor != null) {
-            return simpleInterceptor;
-        }
-        StaticAroundInterceptor staticAroundInterceptor = REGISTRY.getInterceptor0(key);
-        if (staticAroundInterceptor != null) {
-            return staticAroundInterceptor;
-        }
-        Logger logger = Logger.getLogger(InterceptorRegistry.class.getName());
-        if (logger.isLoggable(Level.WARNING)) {
-            logger.warning("interceptor not found. id:" + key);
-        }
-        return DUMMY;
+        return REGISTRY.findInterceptor0(key);
     }
 
     public static int addSimpleInterceptor(SimpleAroundInterceptor interceptor) {
diff --git a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/LoggingInterceptor.java b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/LoggingInterceptor.java
index 58e732bde24e..9ad6777ea144 100644
--- a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/LoggingInterceptor.java
+++ b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/LoggingInterceptor.java
@@ -23,7 +23,7 @@
 /**
  * @author emeroad
  */
-public class LoggingInterceptor implements StaticAroundInterceptor, SimpleAroundInterceptor {
+public class LoggingInterceptor implements StaticAroundInterceptor, SimpleAroundInterceptor, Interceptor {
 
     private final Logger logger;
 
@@ -60,10 +60,7 @@ public void after(Object target, Object[] args, Object result, Throwable throwab
     }
 
     public static String defaultString(final Object object) {
-        if (object == null) {
-            return "null";
-        }
-        return object.toString();
+        return String.valueOf(object);
     }
 
 }
diff --git a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/WeakAtomicReferenceArray.java b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/WeakAtomicReferenceArray.java
new file mode 100644
index 000000000000..2b22326627b2
--- /dev/null
+++ b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/WeakAtomicReferenceArray.java
@@ -0,0 +1,43 @@
+package com.navercorp.pinpoint.bootstrap.interceptor;
+
+import java.util.concurrent.atomic.AtomicReferenceArray;
+
+/**
+ * @author emeroad
+ */
+public final class WeakAtomicReferenceArray<T> {
+
+    private final int length;
+    private final AtomicReferenceArray<T> atomicArray;
+//    private final T[] array;
+
+    public WeakAtomicReferenceArray(int length, Class<? extends T> clazz) {
+        this.length = length;
+        this.atomicArray = new AtomicReferenceArray<T>(length);
+//        this.array = (T[]) Array.newInstance(clazz, length);
+    }
+
+    public final void set(int index, T newValue) {
+        this.atomicArray.set(index, newValue);
+        // need TestCase ~~
+//        this.array[index] = newValue;
+    }
+
+    public final int length() {
+        return length;
+    }
+
+
+    public final T get(int index) {
+        // try not thread safe read  -> fail -> thread safe read
+//        final T unsafeValue = this.array[index];
+//        if (unsafeValue != null) {
+//            return unsafeValue;
+//        }
+//        return (T) this.array[index];
+
+        // thread safe read
+        return this.atomicArray.get(index);
+    }
+
+}
diff --git a/commons/src/main/java/com/navercorp/pinpoint/common/util/DelegateEnumeration.java b/commons/src/main/java/com/navercorp/pinpoint/common/util/DelegateEnumeration.java
index 59fdd5f0f069..5f05f83036e7 100644
--- a/commons/src/main/java/com/navercorp/pinpoint/common/util/DelegateEnumeration.java
+++ b/commons/src/main/java/com/navercorp/pinpoint/common/util/DelegateEnumeration.java
@@ -58,7 +58,7 @@ public boolean hasMoreElements() {
     @Override
     public E nextElement() {
         next(false);
-        // nextExcetpion
+        // nextException
         if (nextException != null) {
             final Exception exception = this.nextException;
             clearNext();


Test patch:
diff --git a/bootstrap-core/src/test/java/com/navercorp/pinpoint/bootstrap/interceptor/AtomicMaxUpdater.java b/bootstrap-core/src/test/java/com/navercorp/pinpoint/bootstrap/interceptor/AtomicMaxUpdater.java
new file mode 100644
index 000000000000..2bf67518386d
--- /dev/null
+++ b/bootstrap-core/src/test/java/com/navercorp/pinpoint/bootstrap/interceptor/AtomicMaxUpdater.java
@@ -0,0 +1,28 @@
+package com.navercorp.pinpoint.bootstrap.interceptor;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author emeroad
+ */
+public class AtomicMaxUpdater {
+
+    private final AtomicInteger maxIndex = new AtomicInteger(-1);
+
+    public boolean updateMax(int max) {
+        while (true) {
+            final int currentMax = maxIndex.get();
+            if (currentMax >= max) {
+                return false;
+            }
+            final boolean update = maxIndex.compareAndSet(currentMax, max);
+            if (update) {
+                return true;
+            }
+        }
+    }
+
+    public int getIndex() {
+        return maxIndex.get();
+    }
+}
diff --git a/bootstrap-core/src/test/java/com/navercorp/pinpoint/bootstrap/interceptor/InterceptorRegistryTest.java b/bootstrap-core/src/test/java/com/navercorp/pinpoint/bootstrap/interceptor/InterceptorRegistryTest.java
new file mode 100644
index 000000000000..208577960766
--- /dev/null
+++ b/bootstrap-core/src/test/java/com/navercorp/pinpoint/bootstrap/interceptor/InterceptorRegistryTest.java
@@ -0,0 +1,84 @@
+package com.navercorp.pinpoint.bootstrap.interceptor;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+
+public class InterceptorRegistryTest {
+
+    @Test
+    public void indexSize_0() {
+
+        try {
+            new InterceptorRegistry(-1);
+            Assert.fail();
+        } catch (IllegalArgumentException ignore) {
+        }
+
+    }
+
+    @Test
+    public void indexSize_1() {
+        try {
+            InterceptorRegistry interceptorRegistry = new InterceptorRegistry(0);
+            StaticAroundInterceptor mock = mock(StaticAroundInterceptor.class);
+            interceptorRegistry.addStaticInterceptor(mock);
+            Assert.fail();
+        } catch (IndexOutOfBoundsException ignore) {
+        }
+    }
+
+    @Test
+    public void indexSize_2() {
+        InterceptorRegistry interceptorRegistry = new InterceptorRegistry(1);
+        interceptorRegistry.addStaticInterceptor(mock(StaticAroundInterceptor.class));
+        try {
+            interceptorRegistry.addStaticInterceptor(mock(StaticAroundInterceptor.class));
+            Assert.fail();
+        } catch (IndexOutOfBoundsException ignore) {
+        }
+    }
+
+    @Test
+    public void addStaticInterceptor()  {
+        StaticAroundInterceptor mock = mock(StaticAroundInterceptor.class);
+
+        InterceptorRegistry registry = new InterceptorRegistry();
+        int key = registry.addStaticInterceptor(mock);
+        StaticAroundInterceptor find = registry.getInterceptor0(key);
+
+        Assert.assertSame(mock, find);
+    }
+
+    @Test
+     public void addSimpleInterceptor() {
+        SimpleAroundInterceptor mock = mock(SimpleAroundInterceptor.class);
+
+        InterceptorRegistry registry = new InterceptorRegistry();
+        int key = registry.addSimpleInterceptor0(mock);
+        SimpleAroundInterceptor find = registry.getSimpleInterceptor0(key);
+
+        Assert.assertSame(mock, find);
+    }
+
+    @Test
+    public void findInterceptor() {
+        SimpleAroundInterceptor mock0 = mock(SimpleAroundInterceptor.class);
+        StaticAroundInterceptor mock1 = mock(StaticAroundInterceptor.class);
+
+        InterceptorRegistry registry = new InterceptorRegistry();
+        int key1 = registry.addSimpleInterceptor0(mock0);
+        Assert.assertEquals(key1, 0);
+        int key2 = registry.addStaticInterceptor(mock1);
+        Assert.assertEquals(key2, 1);
+        Interceptor interceptor0 = registry.findInterceptor0(0);
+        Assert.assertNotNull(interceptor0);
+        Assert.assertSame(interceptor0, mock0);
+        Interceptor interceptor1 = registry.findInterceptor0(1);
+        Assert.assertNotNull(interceptor1);
+        Assert.assertSame(interceptor1, mock1);
+
+    }
+
+}
\ No newline at end of file
diff --git a/bootstrap-core/src/test/java/com/navercorp/pinpoint/bootstrap/interceptor/WeakAtomicReferenceArrayTest.java b/bootstrap-core/src/test/java/com/navercorp/pinpoint/bootstrap/interceptor/WeakAtomicReferenceArrayTest.java
new file mode 100644
index 000000000000..b08d0accbe30
--- /dev/null
+++ b/bootstrap-core/src/test/java/com/navercorp/pinpoint/bootstrap/interceptor/WeakAtomicReferenceArrayTest.java
@@ -0,0 +1,314 @@
+package com.navercorp.pinpoint.bootstrap.interceptor;
+
+import junit.framework.Assert;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * raceCondition generate fail.~~~~ hmm
+ */
+@Ignore
+public class WeakAtomicReferenceArrayTest {
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    private final int arraySize = 1024 * 2000;
+    private final int testMapSize = 1;
+
+    private final Map<Integer, AtomicReferenceTest> map = new HashMap<Integer, AtomicReferenceTest>();
+    private final AtomicInteger nextMapId = new AtomicInteger();
+
+    private int writerThreadSize = 1;
+    private ThreadPoolExecutor writer;
+
+    private int readThreadSize = 2;
+    private ThreadPoolExecutor reader;
+
+    private final AtomicInteger failCounter = new AtomicInteger();
+
+    @Before
+    public void setUp() throws Exception {
+        writer = (ThreadPoolExecutor) Executors.newFixedThreadPool(writerThreadSize);
+        reader = (ThreadPoolExecutor) Executors.newFixedThreadPool(readThreadSize);
+        writer.prestartAllCoreThreads();
+        reader.prestartAllCoreThreads();
+        for (int i = 0; i < testMapSize; i++) {
+            map.put(i, new AtomicReferenceTest());
+        }
+    }
+
+
+    @After
+    public void tearDown() throws Exception {
+        writer.shutdownNow();
+        reader.shutdownNow();
+    }
+
+    static final class Cell {
+        volatile long p0, p1, p2, p3, p4, p5, p6;
+        volatile WeakAtomicReferenceArray<Integer> weakAtomicReferenceArray;
+        volatile long q0, q1, q2, q3, q4, q5, q6;
+    }
+
+    private static class ChangedValue {
+        private int index;
+        private int value;
+
+        public ChangedValue(int index, int value) {
+            this.index = index;
+            this.value = value;
+        }
+    }
+
+    private class AtomicReferenceTest {
+        private final Cell cell = new Cell();
+
+        private final AtomicInteger nextId = new AtomicInteger(0);
+        private final AtomicMaxUpdater maxIndex = new AtomicMaxUpdater();
+//        private final ConcurrentLinkedQueue<Integer> updateIndex = new ConcurrentLinkedQueue<Integer>();
+        private final WeakAtomicReferenceArray<Integer> ref;
+        private final AtomicInteger afterLast = new AtomicInteger(-1);
+        private final AtomicReference<ChangedValue> lastChangeValue = new AtomicReference<ChangedValue>();
+
+
+        private final Random random = new Random();
+
+        public AtomicReferenceTest() {
+            cell.weakAtomicReferenceArray = new WeakAtomicReferenceArray<Integer>(arraySize, Integer.class);
+            ref = cell.weakAtomicReferenceArray;
+        }
+
+        public boolean nextId() {
+
+            int nextId = this.nextId.getAndIncrement();
+            if (nextId < arraySize) {
+                ref.set(nextId, nextId);
+                afterLast.set(nextId);
+//                maxIndex.updateMax(nextId);
+//                updateIndex.offer(nextId);
+//                logger.debug("nextId:{}", nextId);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        public boolean changeId(int index, int value) {
+
+            if (index < arraySize) {
+                ref.set(index, value);
+                lastChangeValue.set(new ChangedValue(index, value));
+//                maxIndex.updateMax(nextId);
+//                updateIndex.offer(nextId);
+//                logger.debug("nextId:{}", nextId);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+
+        public boolean get(final int findIndex) {
+            if (findIndex == -1) {
+                return true;
+            }
+            Integer findResult = ref.get(findIndex);
+            return checkInteger(findIndex, findResult);
+        }
+
+        public boolean checkChangeId() {
+            ChangedValue changedValue = lastChangeValue.get();
+            if (changedValue == null){
+                return true;
+            }
+            Integer findResult = ref.get(changedValue.index);
+            return checkInteger(changedValue.value, findResult);
+        }
+
+        private boolean checkInteger(int findIndex, Integer findResult) {
+            if (findResult == null) {
+                logger.info("null find:{} result:{}", findIndex, nextId.get());
+                return false;
+            }
+            final boolean result = findResult == findIndex;
+            if (!result) {
+                logger.info("not equals findResult:{}, findIndex:{}", findResult, findIndex);
+            }
+            return result;
+        }
+
+//        public boolean testInsert() {
+//            final Integer findIndex = updateIndex.poll();
+//            if (findIndex == null) {
+//                return true;
+//            }
+//            Integer findResult = cell.weakAtomicReferenceArray.get(findIndex);
+//            if (random.nextInt(3) == 0) {
+//                updateIndex.offer(findIndex);
+//            }
+//            return checkInteger(findIndex, findResult);
+//        }
+
+
+
+        public boolean randomGet() {
+            if (writerThreadSize != 1) {
+                return true;
+            }
+//            final int maxIndex = getMaxIndex();
+            final int maxIndex = afterLast.get();
+            if (maxIndex == -1) {
+                return true;
+            }
+            int randomIndex;
+            if (maxIndex == 0) {
+                randomIndex = 0;
+            } else {
+                randomIndex = Math.abs(random.nextInt()) % (maxIndex);
+            }
+            return get(randomIndex);
+        }
+
+        public boolean lastGet() {
+            return get(getMaxIndex());
+        }
+
+        private int getMaxIndex() {
+            return maxIndex.getIndex();
+        }
+
+    }
+
+    @Test
+    public void testLastGet() {
+        AtomicReferenceTest mock = new AtomicReferenceTest();
+        Assert.assertTrue(mock.lastGet());
+
+        mock.nextId();
+        Assert.assertTrue(mock.lastGet());
+        for (int i = 0; i < 10; i++) {
+            Assert.assertTrue(mock.lastGet());
+        }
+    }
+
+    @Test
+    public void testRandomGet() {
+        AtomicReferenceTest mock = new AtomicReferenceTest();
+        Assert.assertTrue(mock.randomGet());
+        mock.nextId();
+        mock.nextId();
+        mock.nextId();
+        Assert.assertTrue(mock.randomGet());
+        for (int i = 0; i < 10; i++) {
+            Assert.assertTrue(mock.randomGet());
+        }
+    }
+
+
+
+    @Test
+    public void testTestMock3() {
+        AtomicReferenceTest mock = new AtomicReferenceTest();
+        mock.nextId();
+        mock.nextId();
+        mock.nextId();
+        Assert.assertTrue(mock.randomGet());
+        for (int i = 0; i < 100; i++) {
+            Assert.assertTrue(mock.randomGet());
+        }
+    }
+
+    @Test
+    public void test() throws Exception {
+        final AtomicBoolean start = new AtomicBoolean(true);
+
+        final Runnable writeJob = new Runnable() {
+            @Override
+            public void run() {
+                logger.info("WriteJob-start");
+                int i =0;
+                while (start.get()) {
+
+                    AtomicReferenceTest referenceTest = getTestMock();
+                    referenceTest.nextId();
+//                    referenceTest.changeId(0, i);
+                    try {
+                        Thread.sleep(10);
+                    } catch (InterruptedException ignore) {
+                    }
+                    i++;
+                }
+                logger.info("WriteJob-end");
+            }
+
+        };
+
+        final Runnable readJob = new Runnable() {
+            @Override
+            public void run() {
+                logger.info("ReaderJob-start");
+                while (start.get()) {
+
+                    AtomicReferenceTest atomicReferenceTest = getTestMock();
+                    if (!atomicReferenceTest.lastGet()) {
+                        failCounter.getAndIncrement();
+                    }
+//                    if (!atomicReferenceTest.checkChangeId()) {
+//                        failCounter.getAndIncrement();
+//                    }
+//                    if (!testMock.testInsert()) {
+//                        failCounter.getAndIncrement();
+//                    }
+//                    if (!testMock.randomGet()) {
+//                        failCounter.getAndIncrement();
+//                    }
+//                    try {
+//                        Thread.sleep(10);
+//                    } catch (InterruptedException e) {
+//                        e.printStackTrace();
+//                    }
+                }
+                logger.info("ReaderJob-end");
+            }
+        };
+
+        for (int i = 0; i < readThreadSize; i++) {
+            reader.execute(readJob);
+        }
+        for (int i = 0; i < writerThreadSize; i++) {
+            writer.execute(writeJob);
+        }
+
+        System.out.println("start");
+        for (int i = 0; i < 100; i++) {
+            Thread.sleep(1000);
+            logger.info("failCounter:{}", failCounter.get());
+        }
+        start.set(false);
+        Thread.sleep(1000);
+        Assert.assertEquals("raceCondition test", failCounter.get(), 0);
+        writer.shutdown();
+        reader.shutdown();
+
+    }
+
+    private AtomicReferenceTest getTestMock() {
+        int andIncrement = nextMapId.getAndIncrement();
+        int mapIndex = andIncrement % testMapSize;
+        return map.get(mapIndex);
+    }
+
+}
\ No newline at end of file
diff --git a/bootstrap-core/src/test/resources/log4j.xml b/bootstrap-core/src/test/resources/log4j.xml
new file mode 100644
index 000000000000..79094a1014e3
--- /dev/null
+++ b/bootstrap-core/src/test/resources/log4j.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
+
+    <appender name="console" class="org.apache.log4j.ConsoleAppender">
+        <layout class="org.apache.log4j.EnhancedPatternLayout">
+            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%-5p](%-35c{1.}:%-3L) %m%n" />
+            <!-- %L(line number) option is extremely slow. -->
+            <!--<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%-5p](%-30c{1}) %m%n" />-->
+        </layout>
+    </appender>
+
+    <logger name="com.navercorp.pinpoint" additivity="false">
+        <level value="DEBUG" />
+        <appender-ref ref="console" />
+    </logger>
+
+    <root>
+        <level value="DEBUG" />
+        <appender-ref ref="console" />
+    </root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/interceptor/InterceptorRegistryTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/interceptor/InterceptorTest.java
similarity index 94%
rename from profiler/src/test/java/com/navercorp/pinpoint/profiler/interceptor/InterceptorRegistryTest.java
rename to profiler/src/test/java/com/navercorp/pinpoint/profiler/interceptor/InterceptorTest.java
index 31c2226be5e6..3b77dedadc76 100644
--- a/profiler/src/test/java/com/navercorp/pinpoint/profiler/interceptor/InterceptorRegistryTest.java
+++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/interceptor/InterceptorTest.java
@@ -34,21 +34,10 @@
 /**
  * @author emeroad
  */
-public class InterceptorRegistryTest {
+public class InterceptorTest {
 
-    private final Logger logger = LoggerFactory.getLogger(InterceptorRegistryTest.class.getName());
+    private final Logger logger = LoggerFactory.getLogger(InterceptorTest.class.getName());
 
-    private InterceptorRegistry registry = new InterceptorRegistry();
-
-    @Test
-    public void addInterceptor() throws Exception {
-        TestBeforeInterceptor interceptor = new TestBeforeInterceptor();
-
-        int key = registry.addInterceptor0(interceptor);
-        Interceptor find = registry.getInterceptor0(key);
-
-        Assert.assertEquals(interceptor, find);
-    }
 
     //    @Test
     public void methodName() throws NoSuchMethodException {