A familiar cache notation:
inch Cached Then return from cacheelse Compute value in cache return
It seems to be logically correct, but it actually poses 2 kinds of problems:
1, this method is not thread-safe.
2, produces the value writes duplicates, causes the wrong data.
For example, thread 1 performs the calculation of the value of the process, threads 2 also enter the data check, the data will be written multiple times, the program is very dangerous.
Demo Error code:
// most prone to error writing, read the cache first, read the write cache Public Long index) { if (Cache.containskey (index)) { return cache. Get(index); } Long 1 2 ); Cache.put (index, value); return value; }
1, the traditional solution, using the Foldback Lock (Getnumberbylock method) or synchronous Lock (Getnumberbysynchroniz method).
Code
import java.util.arraylist;import java.util.hashmap;import java.util.list;import Java.util.map;import Java.util.concurrent.executorservice;import Java.util.concurrent.executors;import java.util.concurrent.Future; Import Java.util.concurrent.timeunit;import Java.util.concurrent.locks.lock;import Java.util.concurrent.locks.ReentrantLock; Public classNaivecacheexample {PrivateFinal Map<long, long> cache =NewHashmap<>(); PrivateObject o=NewObject (); LockLock=NewReentrantlock (); Publicnaivecacheexample () {Cache.put (0L,1L); Cache.put (1L,1L); } //most prone to error writing, read the cache first, read the write cache PublicLong GetNumber (finalLongindex) { if(Cache.containskey (index)) {returnCache.Get(index); } FinalLongValue = GetNumber (Index-1) + getnumber (Index-2); Cache.put (index, value); returnvalue; } //Use the Foldback lock to synchronize the read and write PublicLong Getnumberbylock (finalLongindex) { LongValue =0; Try { Lock.Lock(); if(Cache.containskey (index)) {returnCache.Get(index); } Value= Getnumberbylock (Index-1) + Getnumberbylock (Index-2); Cache.put (index, value); returnvalue; } Catch(Exception e) {}finally { Lock. Unlock (); } return 0l; } //use synchronization to enable read and write synchronization PublicLong Getnumberbysynchroniz (finalLongindex) {synchronized (o) {LongValue =0; Try { if(Cache.containskey (index)) {returnCache.Get(index); } Value= Getnumberbysynchroniz (Index-1) + Getnumberbysynchroniz (Index-2); Cache.put (index, value); returnvalue; } Catch(Exception e) {}finally { } } return 0l; } Public Static voidMain (final string[] args) {naivecacheexample naivecacheexample=Newnaivecacheexample (); Thread Threada=NewThread (NewRunnable () {@Override Public voidrun () {System. out. println (Naivecacheexample.getnumberbysynchroniz ( +)); } } ,"thread-a"); Threada.start (); Final Thread threadb=NewThread (NewRunnable () { Public voidrun () {System. out. println (Naivecacheexample.getnumberbysynchroniz ( +)); } }, "Thread-b"); Threadb.start (); }}
2, a better cache algorithm can be used Callable
and Future
. The cached value is stored in an instance of Concurrentmap, and Concurrentmap is thread-safe.
Code:
Import Java.util.concurrent.callable;import Java.util.concurrent.concurrenthashmap;import Java.util.concurrent.concurrentmap;import Java.util.concurrent.executionexception;import Java.util.concurrent.future;import Java.util.concurrent.FutureTask; Public classGenericcacheexample<k, v> { PrivateFinal concurrentmap<k, future<v>> cache =NewConcurrenthashmap<>(); PrivateFuture<v> createfutureifabsent (Final K key, final callable<v>callable) { Future<V> future = cache.Get(key); if(Future = =NULL) {Final Futuretask<V> Futuretask =NewFuturetask<v>(callable); Future=cache.putifabsent (key, Futuretask); if(Future = =NULL) { future=Futuretask; Futuretask.run (); } } returnFuture ; } PublicV GetValue (Final K key, final callable<v>callable) throws Interruptedexception, executionexception {Try{Final future<V> future =createfutureifabsent (key, callable); returnFuture.Get(); } Catch(Final interruptedexception e) {cache.remove (key); Throwe; } Catch(Final executionexception e) {cache.remove (key); Throwe; } Catch(Final runtimeexception e) {cache.remove (key); Throwe; } } Public voidsetvalueifabsent (Final K key, final V value) {createfutureifabsent (key,NewCallable<v>() {@Override PublicV Call () throws Exception {returnvalue; } }); }}
Reference blog:
http://www.javacreed.com/how-to-cache-results-to-boost-performance/
Write a thread-safe Java cache read-write mechanism