编写线程安全的Java缓存读写机制 (原创)
来源:互联网 发布:忘羡捏脸数据 编辑:程序博客网 时间:2024/06/01 09:29
一种习以为常的缓存写法:
IF value in cached THEN return value from cacheELSE compute value save value in cache return valueEND 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() { @Override public 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>() { @Override public V call() throws Exception { return value; } }); }}
参考博客:
http://www.javacreed.com/how-to-cache-results-to-boost-performance/
0 0
- 编写线程安全的Java缓存读写机制 (原创)
- Java-编写高效的线程安全类
- 编写高效的java线程安全类
- 编写线程安全的Java代码
- JAVA线程安全机制
- java如何实现线程的安全:线程的同步机制
- (原创)确保JAVA线程安全的4种常用方法
- java安全机制 控制文件的授权 读写控制
- 线程安全的缓存代码
- 编写线程安全的JSP
- 编写线程安全的代码
- 编写线程安全的方法
- 编写线程安全的代码
- Java并发:如何编写线程安全的代码
- C++ 异步读写安全的缓存区
- Java练习题-编写一个线程安全的延迟加载单例模式(懒汉模式)
- Java练习题-编写一个线程安全的延迟加载单例模式(懒汉模式)
- 黑马程序员——Java基础---线程的另一个总结(6)--线程读写锁,缓存小例子
- iphone手机safari输入法不支持keyup事件解决方法
- 立体类组共有的抽象类
- 企业IM (或业务系统)web api的json格式设计思考(原创)
- 微服务系统架构的优点与不足
- JIRA Rest JAVA Client API实现问题管理及自定义字段(原创)
- 编写线程安全的Java缓存读写机制 (原创)
- 使用泛型SwingWorker与EDT事件分发线程保持通讯
- Jira API传字符串的换行问题 (文本编辑器使用)
- 读/写锁的实现和应用(高并发状态下的map实现)
- 替换元素节点replaceChild()
- (原创)JAVA阻塞队列LinkedBlockingQueue 以及非阻塞队列ConcurrentLinkedQueue 的区别
- (转)ReentrantLock可重入锁的使用场景
- AIX系统查看apache版本号
- (原创)确保JAVA线程安全的4种常用方法