0

所以我使用 Guava 的 CacheBuilder 作为 ConcurrentLRUCache 意味着这个缓存是线程安全的并且具有 LRU 属性,请参阅CacheBuilder Guava Docs

我的假设是当多个线程同时启动时具有相同的键,一个 CyclicBarrier 用于此,然后一个线程将 put() 放入缓存,而其他线程等待。之后,其余线程将看到一个值已经在缓存中,而不是 put() 到缓存中。

这不适用于下面的代码,因为每个线程都会创建一个新的 Object() 并将其放入缓存中。通过运行测试并查看控制台以查看每次都创建了不同的对象来进行验证。

  • 我使用
    CacheBuilder 的方式有什么本质上的问题吗?
  • 我可以使用更好的方法吗?
  • 有我可以使用的图书馆吗?

请和谢谢!

import java.util.concurrent.CyclicBarrier;

import org.junit.Test;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

public class GuavaLRUCacheTest {
    private Cache<String, Object> concurrentLRUCache = CacheBuilder.newBuilder().maximumSize(100).concurrencyLevel(1).build();

    @Test
    public void test() throws Exception {
        // The gate size is set based on the (number of threads to run) + (1 for the current thread).
        CyclicBarrier gate = new CyclicBarrier(4);

        // Same key is used for all threads
        ConcurrentLRUTestThread t1 = new ConcurrentLRUTestThread(gate, "key1");
        ConcurrentLRUTestThread t2 = new ConcurrentLRUTestThread(gate, "key1");
        ConcurrentLRUTestThread t3 = new ConcurrentLRUTestThread(gate, "key1");

        t1.start();
        t2.start();
        t3.start();

        // Open the gate on all threads.
        gate.await();

        t1.join();
        t2.join();
        t3.join();
    }

    class ConcurrentLRUTestThread extends Thread {
        private CyclicBarrier gate;
        private String key;
        public ConcurrentLRUTestThread(CyclicBarrier gate, String key) {
            this.gate = gate;
            this.key = key;
        }
        @Override
        public void run() {
            try {
                gate.await();
                if (concurrentLRUCache.getIfPresent(key) == null) {
                    System.out.println(">>>>> "+ System.nanoTime() +" - "+Thread.currentThread().getId() + " before put " + concurrentLRUCache.getIfPresent(key));
                    concurrentLRUCache.put(key, new Object());
                    System.out.println(">>>>> "+ System.nanoTime() +" - "+Thread.currentThread().getId() + " after put " + concurrentLRUCache.getIfPresent(key));
                } else{
                    System.out.println(">>>>> "+ System.nanoTime() +" - "+Thread.currentThread().getId() + " else " + concurrentLRUCache.getIfPresent(key));
                }  
            } catch (Throwable x) {
                System.out.println(">>>>> "+ System.currentTimeMillis() +" - "+Thread.currentThread().getId() + " ConcurrentLRUTestThread exception");
            }
        }
    }
}
4

1 回答 1

2

你是第一次打电话cache.getIfPresent,然后尝试打电话cache.put。这将不起作用,因为这不是作为单个原子动作完成的。多个线程可以看到缓存中没有值,这将导致多次cache.put调用。解决此问题的直接方法是创建一个包含检查和操作的关键部分。然后只有一个线程会看到缓存中没有值。幸运的是Cache已经包含了做这件事的方法:Cache.get(key, valueLoader). 只需使用它来检索值:


    public class GuavaLRUCacheTest {

    private Cache concurrentLRUCache = CacheBuilder.newBuilder().maximumSize(100).concurrencyLevel(1).build();

    @Test
    public void test() throws Exception {
        // The gate size is set based on the (number of threads to run) + (1 for the current thread).
        CyclicBarrier gate = new CyclicBarrier(4);

        // Same key is used for all threads
        ConcurrentLRUTestThread t1 = new ConcurrentLRUTestThread(gate, "key1");
        ConcurrentLRUTestThread t2 = new ConcurrentLRUTestThread(gate, "key1");
        ConcurrentLRUTestThread t3 = new ConcurrentLRUTestThread(gate, "key1");

        t1.start();
        t2.start();
        t3.start();

        // Open the gate on all threads.
        gate.await();

        t1.join();
        t2.join();
        t3.join();
    }

    class ConcurrentLRUTestThread extends Thread {
        private CyclicBarrier gate;
        private String key;
        public ConcurrentLRUTestThread(CyclicBarrier gate, String key) {
            this.gate = gate;
            this.key = key;
        }
        @Override
        public void run() {
            try {
                gate.await();
                concurrentLRUCache.get(key, Object::new);
            } catch (Throwable x) {
                System.out.println(">>>>> "+ System.currentTimeMillis() +" - "+Thread.currentThread().getId() + " ConcurrentLRUTestThread exception");
            }
        }
    }
}
于 2016-08-12T08:31:15.557 回答