Skip to content

Commit 2ca7753

Browse files
committed
fix #1261 Replace ReentrantLock with CountDownLatch to avoid OutOfMemoryError
1 parent 487f153 commit 2ca7753

File tree

1 file changed

+22
-18
lines changed

1 file changed

+22
-18
lines changed

src/main/java/org/apache/ibatis/cache/decorators/BlockingCache.java

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2009-2019 the original author or authors.
2+
* Copyright 2009-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,10 +15,10 @@
1515
*/
1616
package org.apache.ibatis.cache.decorators;
1717

18+
import java.text.MessageFormat;
1819
import java.util.concurrent.ConcurrentHashMap;
20+
import java.util.concurrent.CountDownLatch;
1921
import java.util.concurrent.TimeUnit;
20-
import java.util.concurrent.locks.Lock;
21-
import java.util.concurrent.locks.ReentrantLock;
2222

2323
import org.apache.ibatis.cache.Cache;
2424
import org.apache.ibatis.cache.CacheException;
@@ -37,7 +37,7 @@ public class BlockingCache implements Cache {
3737

3838
private long timeout;
3939
private final Cache delegate;
40-
private final ConcurrentHashMap<Object, ReentrantLock> locks;
40+
private final ConcurrentHashMap<Object, CountDownLatch> locks;
4141

4242
public BlockingCache(Cache delegate) {
4343
this.delegate = delegate;
@@ -85,31 +85,35 @@ public void clear() {
8585
delegate.clear();
8686
}
8787

88-
private ReentrantLock getLockForKey(Object key) {
89-
return locks.computeIfAbsent(key, k -> new ReentrantLock());
90-
}
91-
9288
private void acquireLock(Object key) {
93-
Lock lock = getLockForKey(key);
94-
if (timeout > 0) {
89+
CountDownLatch newLatch = new CountDownLatch(1);
90+
while (true) {
91+
CountDownLatch latch = locks.putIfAbsent(key, newLatch);
92+
if (latch == null) {
93+
break;
94+
}
9595
try {
96-
boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
97-
if (!acquired) {
98-
throw new CacheException("Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());
96+
if (timeout > 0) {
97+
boolean acquired = latch.await(timeout, TimeUnit.MILLISECONDS);
98+
if (!acquired) {
99+
throw new CacheException(
100+
"Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());
101+
}
102+
} else {
103+
latch.await();
99104
}
100105
} catch (InterruptedException e) {
101106
throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
102107
}
103-
} else {
104-
lock.lock();
105108
}
106109
}
107110

108111
private void releaseLock(Object key) {
109-
ReentrantLock lock = locks.get(key);
110-
if (lock.isHeldByCurrentThread()) {
111-
lock.unlock();
112+
CountDownLatch latch = locks.remove(key);
113+
if (latch == null) {
114+
throw new IllegalStateException("Detected an attempt at releasing unacquired lock. This should never happen.");
112115
}
116+
latch.countDown();
113117
}
114118

115119
public long getTimeout() {

0 commit comments

Comments
 (0)