编写线程安全的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缓存读写机制 (原创)相关推荐
- 写缓存java,编写线程安全的Java缓存读写机制 (原创)
一种习以为常的缓存写法: IF value in cached THEN return value from cache ELSE compute value save value in cache ...
- java缓存读写文件小例子
在一个叫掏你屋的博客上看到的小例子,缓存读写文件: package com.taoniwu; import java.util.regex.*; import java.io.*; public cl ...
- JAVA多线程读写文件如何做到线程安全?(文件锁,FileChannel)
文章目录 NIO提升性能 多线程读写同一个文件有哪些场景需要同步处理? 使用对文件加锁的方式做到线程安全 写文件线程安全 读文件线程安全 小编写的IOListener接口,用于回调 小编写的IOUti ...
- Java中的自增操作符与中间缓存变量机制
2019独角兽企业重金招聘Python工程师标准>>> 作者:MaggieDorami 我们来看这样一段程序: public static void main(String[] ar ...
- java中间缓存变量机制_Java中间缓存变量机制
j++,是一个依赖于java里面的"中间缓存变量机制"来实现的, ++在前就是"先加后赋"(++j) ++在后就是"先赋后加" (j++) ...
- Map实现java缓存机制的简单实例
缓存是Java中主要的内容,主要目的是缓解项目访问数据库的压力以及提升访问数据的效率,以下是通过Map实现java缓存的功能,并没有用cache相关框架. 一.缓存管理类 CacheMgr.java ...
- linux 文件缓存大小设置,Linux文件读写机制及优化方式
导读 Linux是一个可控性强的,安全高效的操作系统.本文只讨论Linux下文件的读写机制,不涉及不同读取方式如read,fread,cin等的对比,这些读取方式本质上都是调用系统api read,只 ...
- 剑指offer之java缓存总结,从单机缓存到分布式缓存架构
1.缓存定义 高速数据存储层,提高程序性能 2.为什么要用缓存(读多写少,高并发) 1.提高读取吞吐量 2.提升应用程序性能 3.降低数据库成本 4.减少后端负载 5.消除数据库热点 6.可预测的性能 ...
- 【转载】Java JVM 运行机制及基本原理
原博地址:https://zhuanlan.zhihu.com/p/25713880 JVM的基础概念 JVM的中文名称叫Java虚拟机,它是由软件技术模拟出计算机运行的一个虚拟的计算机. JVM也充 ...
最新文章
- X86嵌入式主板在IOT网关产品的应用
- (20):数据的准备工作
- 2、Eternal框架-svn_有更新!
- photoshop cc_如何使用Photoshop CC将图片变成卡通
- 蔚来测开提前批面试(一面)
- 如何启用×××服务器端的IPsec功能
- Android -- Property Animation
- 红帽linux进阶,Linux进阶第二天
- FISCO BCOS源码(4)第三方依赖和模块
- java实现文件移动_java实现文件的复制,移动
- CRC码计算及校验原理计算
- 用R语言下载任意地区DEM数据
- Linux学习—编译
- WPF教程二:理解WPF的布局系统和常用的Panel布局
- 酷!有人把火星车都造出来了,教程全面开源
- Linux终端更改字体
- 云计算与大数据概论第十一周(2)
- python加权最小二乘_如何计算加权最小二乘法的样本权重?
- adb模拟按键home_adb命令中 模拟按键 KeyCode 部分详解
- 【UnityShader】光线追踪体积光