2016-08-11 2 views
0

also verwende CacheBuilder von Guava als ConcurrentLRUCache, was bedeutet, dass dieser Cache threadsicher ist und LRU-Eigenschaften hat, siehe CacheBuilder Guava Docs.Wie verwende ich CacheBuiler von Guava als ConcurrentLRUCache

Meine Annahme ist, wenn mehrere Threads gleichzeitig mit dem gleichen Schlüssel gestartet werden, ein CyclicBarrier wird dafür verwendet, dann wird ein Thread put() in den Cache, während die anderen warten. Danach sehen die verbleibenden Threads, dass ein Wert bereits im Cache ist und nicht put() in den Cache.

Dies gilt nicht für den folgenden Code, da jeder Thread ein neues Object() erstellt und in den Cache legt. Überprüfen Sie, indem Sie den Test ausführen und auf die Konsole schauen, um zu sehen, dass jedes Mal verschiedene Objekte erstellt werden.

  • Gibt es etwas von Natur aus falsch mit der Art, wie ich den
    CacheBuilder bin mit?
  • Gibt es bessere Methoden, die ich verwenden kann?
  • Gibt es eine Bibliothek, die ich benutzen kann?

Bitte und danke!

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"); 
      } 
     } 
    } 
} 
+3

Verwenden Sie 'LoadingCache', oder zumindest' get (key, callable) '. Wenn Sie auf JDK8 sind, könnten Sie meinen [rewrite] (https://github.com/ben-manes/caffeine) betrachten. –

+3

Sie haben keine Synchronisation zwischen 'getIfPresent' und' put', so dass alle Threads leicht in den ersten 'if'-Zweig gelangen können, während keiner von ihnen' put' aufgerufen hat und dann jeder von ihnen 'put' ausführt. – user3707125

Antwort

2

Sie sind erster Anruf cache.getIfPresent und dann versuchen, cache.put zu nennen. Dies wird nicht funktionieren, da dies nicht als einzelne atomare Aktion durchgeführt wurde. Mehrere Threads können sehen, dass im Cache keine Werte vorhanden sind, was zu mehreren Aufrufen führen wird. Eine einfache Möglichkeit, dies zu beheben, wäre, einen kritischen Abschnitt zu erstellen, der eine Überprüfung und eine Aktion beinhaltet. Dann wird nur ein Thread die Abwesenheit von Wert im Cache sehen. Glücklicherweise enthält Cache bereits Methode, die die Sache macht: Cache.get(key, valueLoader). Verwenden Sie es einfach, um den Wert abzurufen:


    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"); 
      } 
     } 
    } 
} 
Verwandte Themen