编写线程安全的Java缓存读写机制 (原创)
生活随笔
收集整理的這篇文章主要介紹了
编写线程安全的Java缓存读写机制 (原创)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一種習以為常的緩存寫法:
IF value in cached THENreturn value from cache ELSEcompute valuesave value in cachereturn value END IF看上去邏輯無比正確,但實際上會造成2種問題:
1、這種方法是不線程安全的。
2、產生數值寫入重復,造成錯誤的數據。
如下圖,在線程1執行計算數值的過程中,線程2也進入數據檢查,將多次寫入數據,程序非常危險。
?
?
演示錯誤代碼:
//最容易產生的錯誤寫法,先讀取緩存,讀不到就寫緩存public Long getNumber(final long index) {if (cache.containsKey(index)) {return cache.get(index);}final long value = getNumber(index - 1) + getNumber(index - 2);cache.put(index, value);return value;}?
1、傳統的解決辦法,使用重入鎖 (getNumberByLock 方法)或者同步鎖(getNumberBySynchroniz 方法)。
代碼
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 class NaiveCacheExample {private final Map<Long, Long> cache = new HashMap<>();private Object o=new Object();Lock lock =new ReentrantLock();public NaiveCacheExample() {cache.put(0L, 1L);cache.put(1L, 1L);}//最容易產生的錯誤寫法,先讀取緩存,讀不到就寫緩存public Long getNumber(final long index) {if (cache.containsKey(index)) {return cache.get(index);}final long value = getNumber(index - 1) + getNumber(index - 2);cache.put(index, value);return value;}//使用折返鎖,使讀寫同步public Long getNumberByLock(final long index) { long value =0;try {lock.lock(); if (cache.containsKey(index)) {return cache.get(index);}value = getNumberByLock(index - 1) + getNumberByLock(index - 2);cache.put(index, value);return value;}catch (Exception e){}finally{lock.unlock();}return 0l;}//使用同步,使讀寫同步public Long getNumberBySynchroniz(final long index) {synchronized (o){long value =0;try {if (cache.containsKey(index)) {return cache.get(index);}value = getNumberBySynchroniz(index - 1) + getNumberBySynchroniz(index - 2);cache.put(index, value);return value;}catch (Exception e){}finally{}}return 0l;}public static void main(final String[] args) {NaiveCacheExample naiveCacheExample =new NaiveCacheExample();Thread threadA =new Thread(new Runnable(){@Overridepublic void run() {System.out.println(naiveCacheExample.getNumberBySynchroniz(1000)); }},"Thread-A");threadA.start();final Thread threadB = new Thread(new Runnable() {public void run() {System.out.println(naiveCacheExample.getNumberBySynchroniz(1000));}}, "Thread-B");threadB.start();} }?
?
2、一個更好的緩存算法可以用?Callable?和 Future?。 緩存的值將存儲在一個實例 ConcurrentMap 中 ,ConcurrentMap 是線程安全的。
代碼:
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 class GenericCacheExample<K, V> {private final ConcurrentMap<K, Future<V>> cache = new ConcurrentHashMap<>();private Future<V> createFutureIfAbsent(final K key, final Callable<V> callable) {Future<V> future = cache.get(key);if (future == null) {final FutureTask<V> futureTask = new FutureTask<V>(callable);future = cache.putIfAbsent(key, futureTask);if (future == null) {future = futureTask;futureTask.run();}}return future;}public V getValue(final K key, final Callable<V> callable) throws InterruptedException, ExecutionException {try {final Future<V> future = createFutureIfAbsent(key, callable);return future.get();} catch (final InterruptedException e) {cache.remove(key);throw e;} catch (final ExecutionException e) {cache.remove(key);throw e;} catch (final RuntimeException e) {cache.remove(key);throw e;}}public void setValueIfAbsent(final K key, final V value) {createFutureIfAbsent(key, new Callable<V>() {@Overridepublic V call() throws Exception {return value;}});}}?
參考博客:
http://www.javacreed.com/how-to-cache-results-to-boost-performance/
轉載于:https://www.cnblogs.com/starcrm/p/4968223.html
總結
以上是生活随笔為你收集整理的编写线程安全的Java缓存读写机制 (原创)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里云+wordpress搭建个人博客网
- 下一篇: 未能加载文件或程序集“XXX”或它的某一