DATAREDIS-452 - Improve thread synchronization in RedisCacheTest.testCacheGetSynchronized.

Original Pull Request: #165
This commit is contained in:
Mark Paluch
2016-02-05 08:25:16 +01:00
committed by Christoph Strobl
parent 707005e77b
commit be40b0853a
2 changed files with 61 additions and 36 deletions

View File

@@ -16,6 +16,7 @@
package org.springframework.data.redis.cache;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.*;
import static org.hamcrest.core.IsInstanceOf.*;
import static org.hamcrest.core.IsNot.*;
@@ -26,14 +27,13 @@ import static org.junit.Assume.*;
import static org.springframework.data.redis.matcher.RedisTestMatchers.*;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import edu.umd.cs.mtc.MultithreadedTestCase;
import edu.umd.cs.mtc.TestFramework;
import org.hamcrest.core.IsEqual;
import org.junit.AfterClass;
import org.junit.Before;
@@ -44,8 +44,8 @@ import org.junit.runners.Parameterized.Parameters;
import org.springframework.cache.Cache;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.data.redis.ConnectionFactoryTracker;
import org.springframework.data.redis.LongObjectFactory;
import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.StringObjectFactory;
import org.springframework.data.redis.core.AbstractOperationsTestParams;
import org.springframework.data.redis.core.RedisTemplate;
@@ -53,6 +53,7 @@ import org.springframework.data.redis.core.RedisTemplate;
* @author Costin Leau
* @author Jennifer Hickey
* @author Christoph Strobl
* @author Mark Paluch
*/
@SuppressWarnings("rawtypes")
@RunWith(Parameterized.class)
@@ -283,50 +284,66 @@ public class RedisCacheTest extends AbstractNativeCacheTest<RedisTemplate> {
/**
* @see DATAREDIS-443
* @see DATAREDIS-452
*/
@Test
public void testCacheGetSynchronized() throws InterruptedException {
public void testCacheGetSynchronized() throws Throwable {
assumeThat(cache, instanceOf(RedisCache.class));
assumeThat(valueFactory, instanceOf(LongObjectFactory.class));
assumeThat(valueFactory, instanceOf(StringObjectFactory.class));
int threadCount = 10;
final AtomicLong counter = new AtomicLong();
final List<Object> results = new CopyOnWriteArrayList<Object>();
final CountDownLatch latch = new CountDownLatch(threadCount);
TestFramework.runOnce(new CacheGetWithValueLoaderIsThreadSafe((RedisCache) cache));
}
final RedisCache redisCache = (RedisCache) cache;
static class CacheGetWithValueLoaderIsThreadSafe extends MultithreadedTestCase {
final Object key = getKey();
RedisCache redisCache;
TestCacheLoader<String> cacheLoader;
Runnable run = new Runnable() {
@Override
public void run() {
try {
Long value = redisCache.get(key, new Callable<Long>() {
@Override
public Long call() throws Exception {
public CacheGetWithValueLoaderIsThreadSafe(RedisCache redisCache) {
this.redisCache = redisCache;
Thread.sleep(333); // make sure the thread will overlap
return counter.incrementAndGet();
}
});
results.add(value);
} finally {
latch.countDown();
cacheLoader = new TestCacheLoader<String>("test"){
@Override
public String call() throws Exception {
waitForTick(2);
return super.call();
}
}
};
for (int i = 0; i < threadCount; i++) {
new Thread(run).start();
Thread.sleep(100);
};
}
latch.await();
assertThat(results.size(), IsEqual.equalTo(threadCount));
for (Object result : results) {
assertThat((Long) result, equalTo(1L));
public void thread1(){
assertTick(0);
Thread.currentThread().setName(getClass().getSimpleName() + " Cache Loader Thread");
String result = redisCache.get("key", cacheLoader);
assertThat(result, is("test"));
}
public void thread2(){
waitForTick(1);
Thread.currentThread().setName(getClass().getSimpleName() + " Cache Reader Thread");
String result = redisCache.get("key", new TestCacheLoader<String>("illegal value"));
assertThat(result, is("test"));
assertTick(2);
}
}
protected static class TestCacheLoader<T> implements Callable<T> {
private final T value;
public TestCacheLoader(T value) {
this.value = value;
}
@Override
public T call() throws Exception {
return value;
}
}
}