-
Notifications
You must be signed in to change notification settings - Fork 11.1k
Description
Guava Version
30.1
Description
Concurrency problems caused by using guava cache's get() and getIfPresent() methods under multithreading
Example
`import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
public class Main {
final Cache<String, Map<Integer, Integer>> cache = CacheBuilder.newBuilder().initialCapacity(100).build();
public static void main(String[] args) throws InterruptedException {
CountDownLatch startLatch = new CountDownLatch(1);
int count = 10;
Main main2 = new Main();
CountDownLatch doneLatch = new CountDownLatch(count);
for (int i = 0; i < count; i++) {
Thread thread = new Thread(() -> {
try {
startLatch.await();
main2.test();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
} finally {
doneLatch.countDown();
}
});
thread.setName("T" + (i + 1));
thread.start();
}
startLatch.countDown();
doneLatch.await();
}
public void test() throws ExecutionException {
Map<Integer, Integer> target = cache.get("test", HashMap::new);
int i = target.size();
for (; i < 5; i++) {
target.put(i, i);
}
System.out.println(target + ":" + cache.getIfPresent("test"));
}
}`
result:
{0=0, 4=4, 1=1, 2=2, 3=3}:null
{0=0, 4=4, 1=1, 2=2, 3=3}:null
{0=0, 4=4, 1=1, 2=2, 3=3}:null
{0=0, 4=4, 1=1, 2=2, 3=3}:null
{0=0, 4=4, 1=1, 2=2, 3=3}:null
{0=0, 4=4, 1=1, 2=2, 3=3}:null
{0=0, 4=4, 1=1, 2=2, 3=3}:null
{0=0, 4=4, 1=1, 2=2, 3=3}:null
{0=0, 4=4, 1=1, 2=2, 3=3}:null
{0=0, 4=4, 1=1, 2=2, 3=3}:{0=0, 4=4, 1=1, 2=2, 3=3}Expected Behavior
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
public class Main1 {
final Cache<String, Map<Integer, Integer>> cache = Caffeine.newBuilder().build();
public static void main(String[] args) throws InterruptedException {
CountDownLatch startLatch = new CountDownLatch(1);
int count = 10;
Main1 main2 = new Main1();
CountDownLatch doneLatch = new CountDownLatch(count);
for (int i = 0; i < count; i++) {
Thread thread = new Thread(() -> {
try {
startLatch.await();
main2.test();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
} finally {
doneLatch.countDown();
}
});
thread.setName("T" + (i + 1));
thread.start();
}
startLatch.countDown();
doneLatch.await();
}
public void test() throws ExecutionException {
Map<Integer, Integer> target = cache.get("test", s -> new HashMap<>());
int i = target.size();
for (; i < 5; i++) {
target.put(i, i);
}
System.out.println(target + ":" + cache.getIfPresent("test"));
}
}
result:
{0=0, 1=1, 2=2, 3=3, 4=4}:{0=0, 1=1, 2=2, 3=3, 4=4}
{0=0, 1=1, 2=2, 3=3, 4=4}:{0=0, 1=1, 2=2, 3=3, 4=4}
{0=0, 1=1, 2=2, 3=3, 4=4}:{0=0, 1=1, 2=2, 3=3, 4=4}
{0=0, 1=1, 2=2, 3=3, 4=4}:{0=0, 1=1, 2=2, 3=3, 4=4}
{0=0, 1=1, 2=2, 3=3, 4=4}:{0=0, 1=1, 2=2, 3=3, 4=4}
{0=0, 1=1, 2=2, 3=3, 4=4}:{0=0, 1=1, 2=2, 3=3, 4=4}
{0=0, 1=1, 2=2, 3=3, 4=4}:{0=0, 1=1, 2=2, 3=3, 4=4}
{0=0, 1=1, 2=2, 3=3, 4=4}:{0=0, 1=1, 2=2, 3=3, 4=4}
{0=0, 1=1, 2=2, 3=3, 4=4}:{0=0, 1=1, 2=2, 3=3, 4=4}
{0=0, 1=1, 2=2, 3=3, 4=4}:{0=0, 1=1, 2=2, 3=3, 4=4}
Actual Behavior
getIfPresent cannot be immediately visible
Packages
com.google.common.cache
Platforms
Java 8
Checklist
-
I agree to follow the code of conduct.
-
I can reproduce the bug with the latest version of Guava available.