You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2018/03/03 17:56:09 UTC
groovy git commit: Improve the performance of
`ConcurrentCommonCache`(3.0.0+ Only)
Repository: groovy
Updated Branches:
refs/heads/master bf1dc7b1b -> 8e6dd80e1
Improve the performance of `ConcurrentCommonCache`(3.0.0+ Only)
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/8e6dd80e
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/8e6dd80e
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/8e6dd80e
Branch: refs/heads/master
Commit: 8e6dd80e1114b66e1ca482e5ddd436733e532fc2
Parents: bf1dc7b
Author: danielsun1106 <re...@hotmail.com>
Authored: Sun Mar 4 01:56:02 2018 +0800
Committer: danielsun1106 <re...@hotmail.com>
Committed: Sun Mar 4 01:56:02 2018 +0800
----------------------------------------------------------------------
.../runtime/memoize/ConcurrentCommonCache.java | 65 ++++++++++++--------
.../memoize/ConcurrentCommonCacheTest.java | 38 ++++++++++++
2 files changed, 77 insertions(+), 26 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/8e6dd80e/src/main/java/org/codehaus/groovy/runtime/memoize/ConcurrentCommonCache.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/memoize/ConcurrentCommonCache.java b/src/main/java/org/codehaus/groovy/runtime/memoize/ConcurrentCommonCache.java
index 27ee102..a758eb2 100644
--- a/src/main/java/org/codehaus/groovy/runtime/memoize/ConcurrentCommonCache.java
+++ b/src/main/java/org/codehaus/groovy/runtime/memoize/ConcurrentCommonCache.java
@@ -23,7 +23,7 @@ import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.locks.StampedLock;
/**
* Represents a simple key-value cache, which is thread safe and backed by a {@link java.util.Map} instance
@@ -36,9 +36,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ConcurrentCommonCache<K, V> implements EvictableCache<K, V>, ValueConvertable<V, Object>, Serializable {
private static final long serialVersionUID = -7352338549333024936L;
- private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
- private final ReentrantReadWriteLock.ReadLock readLock = rwl.readLock();
- private final ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock();
+ private final StampedLock sl = new StampedLock();
private final CommonCache<K, V> commonCache;
/**
@@ -116,35 +114,43 @@ public class ConcurrentCommonCache<K, V> implements EvictableCache<K, V>, ValueC
public V getAndPut(K key, ValueProvider<? super K, ? extends V> valueProvider, boolean shouldCache) {
V value;
- readLock.lock();
+ long stamp = sl.readLock();
try {
value = commonCache.get(key);
if (null != convertValue(value)) {
return value;
}
- } finally {
- readLock.unlock();
- }
- writeLock.lock();
- try {
- // try to find the cached value again
- value = commonCache.get(key);
- if (null != convertValue(value)) {
- return value;
- }
+ long ws = sl.tryConvertToWriteLock(stamp);
+ if (0L == ws) { // Failed to convert read lock to write lock
+ sl.unlockRead(stamp);
+ stamp = sl.writeLock();
- value = null == valueProvider ? null : valueProvider.provide(key);
- if (shouldCache && null != convertValue(value)) {
- commonCache.put(key, value);
+ // try to find the cached value again
+ value = commonCache.get(key);
+ if (null != convertValue(value)) {
+ return value;
+ }
+ } else {
+ stamp = ws;
}
+
+ value = compute(key, valueProvider, shouldCache);
} finally {
- writeLock.unlock();
+ sl.unlock(stamp);
}
return value;
}
+ private V compute(K key, ValueProvider<? super K, ? extends V> valueProvider, boolean shouldCache) {
+ V value = null == valueProvider ? null : valueProvider.provide(key);
+ if (shouldCache && null != convertValue(value)) {
+ commonCache.put(key, value);
+ }
+ return value;
+ }
+
/**
* {@inheritDoc}
*/
@@ -217,11 +223,11 @@ public class ConcurrentCommonCache<K, V> implements EvictableCache<K, V>, ValueC
* @param action the content to complete
*/
public <R> R doWithWriteLock(Action<K, V, R> action) {
- writeLock.lock();
+ long stamp = sl.writeLock();
try {
return action.doWith(commonCache);
} finally {
- writeLock.unlock();
+ sl.unlockWrite(stamp);
}
}
@@ -230,12 +236,19 @@ public class ConcurrentCommonCache<K, V> implements EvictableCache<K, V>, ValueC
* @param action the content to complete
*/
public <R> R doWithReadLock(Action<K, V, R> action) {
- readLock.lock();
- try {
- return action.doWith(commonCache);
- } finally {
- readLock.unlock();
+ long stamp = sl.tryOptimisticRead();
+ R result = action.doWith(commonCache);
+
+ if (!sl.validate(stamp)) {
+ stamp = sl.readLock();
+ try {
+ result = action.doWith(commonCache);
+ } finally {
+ sl.unlockRead(stamp);
+ }
}
+
+ return result;
}
@FunctionalInterface
http://git-wip-us.apache.org/repos/asf/groovy/blob/8e6dd80e/src/test/org/codehaus/groovy/runtime/memoize/ConcurrentCommonCacheTest.java
----------------------------------------------------------------------
diff --git a/src/test/org/codehaus/groovy/runtime/memoize/ConcurrentCommonCacheTest.java b/src/test/org/codehaus/groovy/runtime/memoize/ConcurrentCommonCacheTest.java
index 44c9ae2..d012e99 100644
--- a/src/test/org/codehaus/groovy/runtime/memoize/ConcurrentCommonCacheTest.java
+++ b/src/test/org/codehaus/groovy/runtime/memoize/ConcurrentCommonCacheTest.java
@@ -19,11 +19,19 @@
package org.codehaus.groovy.runtime.memoize;
import org.apache.groovy.util.Maps;
+import org.apache.groovy.util.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
import java.util.LinkedHashMap;
+import java.util.TreeSet;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
public class ConcurrentCommonCacheTest {
@Test
@@ -200,4 +208,34 @@ public class ConcurrentCommonCacheTest {
Assert.assertEquals("3", sc.get("c"));
Assert.assertEquals("5", sc.get("d"));
}
+
+ @Test
+ public void testAccessCacheConcurrently() throws InterruptedException {
+ final ConcurrentCommonCache<Integer, Integer> m = new ConcurrentCommonCache<>();
+
+ final int threadNum = 60;
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ final CountDownLatch countDownLatch2 = new CountDownLatch(threadNum);
+
+ final AtomicInteger cnt = new AtomicInteger(0);
+
+ for (int i = 0; i < threadNum; i++) {
+ new Thread(() -> {
+ try {
+ countDownLatch.await();
+
+ m.getAndPut(123, k -> cnt.getAndIncrement());
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ countDownLatch2.countDown();
+ }
+ }).start();
+ }
+
+ countDownLatch.countDown();
+ countDownLatch2.await();
+
+ Assert.assertEquals(1, cnt.get());
+ }
}