/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.citrus.util.internal;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicInteger;
import com.alibaba.citrus.util.internal.LazyLoader.ExceptionHandler;
import com.alibaba.citrus.util.internal.LazyLoader.Loader;
import org.junit.Test;
/**
* 测试<code>LazyLoader</code>。
*
* @author Michael Zhou
*/
public class LazyLoaderTests {
private final static int CONCURRENCY = 100;
private final static int LOOP = 1000;
private final static int PERFORMANCE_LOOP = 100000;
private LazyLoader<Resource, Object> lazyLoader;
private Throwable failed;
@Test
public void performance() throws Exception {
PerformanceResult[] sum = new PerformanceResult[3];
sum[0] = performance_test(CONCURRENCY, PERFORMANCE_LOOP, LoaderType.SYNC_LOADER);
sum[1] = performance_test(CONCURRENCY, PERFORMANCE_LOOP, LoaderType.PERTHREAD_LOADER);
sum[2] = performance_test(CONCURRENCY, PERFORMANCE_LOOP, LoaderType.DCL_LOADER);
Arrays.sort(sum);
System.out.println("Performance Summary:");
System.out.printf(" %s > %s > %s\n", sum[0], sum[1], sum[2]);
for (int i = 0; i < sum.length - 1; i++) {
for (int j = 1; j < sum.length; j++) {
if (i != j) {
System.out.printf(" %s is %.2f times faster than %s\n", sum[i].name, sum[j].totalTime
/ (double) sum[i].totalTime, sum[j].name);
}
}
}
System.out.println("------------------------------------");
}
private PerformanceResult performance_test(final int concurrency, final int loop, final LoaderType loaderType)
throws InterruptedException {
final long[] start = new long[1];
Thread[] threads = new Thread[concurrency];
lazyLoader = loaderType.getLoader();
final CyclicBarrier startBarrier = new CyclicBarrier(concurrency, new Runnable() {
public void run() {
start[0] = System.currentTimeMillis();
}
});
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Runnable() {
public void run() {
try {
startBarrier.await();
} catch (Exception e) {
}
try {
for (int i = 0; i < loop && failed == null; i++) {
if (!lazyLoader.testInstance()) {
lazyLoader.getInstance();
assertTrue(lazyLoader.testInstance());
}
}
} catch (Throwable e) {
failed = e;
}
}
}, "thread-" + (i + 1));
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
if (failed != null) {
throw new RuntimeException(failed);
}
long totalTime = System.currentTimeMillis() - start[0];
System.out.printf("%s performance test, concurrency %,d, loop %,d, takes %,d ms\n", loaderType, concurrency,
loop, totalTime);
System.out.printf("Creator thread: %s\n", lazyLoader.getInstance().creator);
System.out.println("------------------------------------");
return new PerformanceResult(loaderType, totalTime);
}
@Test
public void correctness() throws Exception {
correctness_test(CONCURRENCY, LOOP, LoaderType.SYNC_LOADER);
correctness_test(CONCURRENCY, LOOP, LoaderType.PERTHREAD_LOADER);
correctness_test(CONCURRENCY, LOOP, LoaderType.DCL_LOADER);
}
/**
* 确保:
* <ul>
* <li>只初始化一次</li>
* <li>所有线程都能拿到相同的初始化后的对象</li>
* <li>执行顺序不会紊乱,即拿到未初始化完成的对象</li>
* </ul>
*/
private void correctness_test(final int concurrency, final int loop, final LoaderType loaderType)
throws InterruptedException {
long start = System.currentTimeMillis();
Thread[] threads = new Thread[concurrency];
final Map<String, Integer> creators = createLinkedHashMap();
final Resource[] results = new Resource[concurrency];
final AtomicInteger counter1 = new AtomicInteger();
final AtomicInteger counter2 = new AtomicInteger();
final CyclicBarrier startBarrier = new CyclicBarrier(concurrency, new Runnable() {
public void run() {
lazyLoader = loaderType.getLoader(); // 清空loader,准备重新装载
counter1.incrementAndGet();
assertFalse(lazyLoader.testInstance());
}
});
final CyclicBarrier barrier = new CyclicBarrier(concurrency, new Runnable() {
public void run() {
assertTrue(lazyLoader.testInstance());
Resource r = results[0];
assertNotNull(r);
assertNotNull(r.innerObject);
String creatorThread = lazyLoader.getInstance().creator;
creators.put(creatorThread, creators.get(creatorThread) + 1);
for (int i = 1; i < results.length; i++) {
Resource ri = results[i];
assertSame(r, ri);
}
counter2.incrementAndGet();
}
});
for (int i = 0; i < threads.length; i++) {
final int index = i;
String threadName = "thread-" + (i + 1);
creators.put(threadName, 0);
threads[i] = new Thread(new Runnable() {
public void run() {
try {
for (int i = 0; i < loop && failed == null; i++) {
startBarrier.await(); // 齐步,走!
results[index] = lazyLoader.getInstance();
assertNotNull(results[index]);
assertNotNull(results[index].innerObject);
barrier.await(); // 检查结果
}
} catch (Throwable e) {
failed = e;
}
}
}, threadName);
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
if (failed != null) {
throw new RuntimeException(failed);
}
assertEquals(loop, counter1.intValue());
assertEquals(loop, counter2.intValue());
System.out.printf("%s test duration %,d ms\n", loaderType, System.currentTimeMillis() - start);
// 打印creator分布图
int i = 0;
for (int count : creators.values()) {
System.out.printf("%4d ", count);
if (++i % 20 == 0) {
System.out.println();
}
}
System.out.println();
System.out.println("------------------------------------");
}
@Test
public void exception() throws Exception {
exception_test(LoaderType.SYNC_LOADER);
exception_test(LoaderType.PERTHREAD_LOADER);
exception_test(LoaderType.DCL_LOADER);
}
/** 测试exception handler。 */
private void exception_test(final LoaderType loaderType) throws InterruptedException {
LazyLoader<Object, Object> loader;
// without exception handler
loader = loaderType.getLoader(new Loader<Object, Object>() {
public Object load(Object context) {
throw new RuntimeException("test");
}
});
try {
loader.getInstance();
fail();
} catch (Exception e) {
}
assertFalse(loader.testInstance());
try {
loader.getInstance();
fail();
} catch (Exception e) {
}
assertFalse(loader.testInstance());
// with exception handler
loader = loaderType.getLoader(new ExceptionHandler<Object, Object>() {
public Object load(Object context) {
throw new RuntimeException("test");
}
public Object handle(RuntimeException e, Object context) {
return e;
}
});
Object o = loader.getInstance();
assertTrue(o instanceof RuntimeException);
assertEquals("test", ((RuntimeException) o).getMessage());
assertTrue(loader.testInstance());
assertSame(o, loader.getInstance());
}
@Test
public void _toString() {
to_string_test(LoaderType.DCL_LOADER);
to_string_test(LoaderType.PERTHREAD_LOADER);
to_string_test(LoaderType.SYNC_LOADER);
}
private void to_string_test(LoaderType loaderType) {
lazyLoader = loaderType.getLoader();
assertEquals("LazyLoader(LazyLoaderTests.ResourceLoader)", lazyLoader.toString());
lazyLoader.getInstance();
assertEquals("LazyLoader(LazyLoaderTests.ResourceLoader, loaded)", lazyLoader.toString());
}
/** 测试资源类。 */
private static class Resource {
public Object innerObject;
public String creator;
}
/** Loader类型。 */
private static enum LoaderType {
SYNC_LOADER {
@Override
protected <T> LazyLoader<T, Object> getLoader(Loader<T, Object> loader) {
return LazyLoader.getSynchronizedLazyLoader(loader);
}
},
PERTHREAD_LOADER {
@Override
protected <T> LazyLoader<T, Object> getLoader(Loader<T, Object> loader) {
return LazyLoader.getPerThreadLazyLoader(loader);
}
},
DCL_LOADER {
@Override
protected <T> LazyLoader<T, Object> getLoader(Loader<T, Object> loader) {
return LazyLoader.getDoubleCheckedLockingLazyLoader(loader);
}
};
public final LazyLoader<Resource, Object> getLoader() {
return getLoader(new ResourceLoader());
}
protected abstract <T> LazyLoader<T, Object> getLoader(Loader<T, Object> loader);
}
/** 实际的loader。 */
private static class ResourceLoader implements Loader<Resource, Object> {
public Resource load(Object context) {
Resource r = new Resource();
r.innerObject = new Object();
r.creator = Thread.currentThread().getName();
return r;
}
}
/** 性能数据。 */
private static class PerformanceResult implements Comparable<PerformanceResult> {
public final String name;
public final long totalTime;
public PerformanceResult(LoaderType loader, long totalTime) {
this.name = loader.name();
this.totalTime = totalTime;
}
@Override
public String toString() {
return String.format("%s(%,d ms)", name, totalTime);
}
public int compareTo(PerformanceResult o) {
return (int) (totalTime - o.totalTime);
}
}
}