一种习以为常的缓存写法:

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缓存读写机制 (原创)相关推荐

  1. 写缓存java,编写线程安全的Java缓存读写机制 (原创)

    一种习以为常的缓存写法: IF value in cached THEN return value from cache ELSE compute value save value in cache ...

  2. java缓存读写文件小例子

    在一个叫掏你屋的博客上看到的小例子,缓存读写文件: package com.taoniwu; import java.util.regex.*; import java.io.*; public cl ...

  3. JAVA多线程读写文件如何做到线程安全?(文件锁,FileChannel)

    文章目录 NIO提升性能 多线程读写同一个文件有哪些场景需要同步处理? 使用对文件加锁的方式做到线程安全 写文件线程安全 读文件线程安全 小编写的IOListener接口,用于回调 小编写的IOUti ...

  4. Java中的自增操作符与中间缓存变量机制

    2019独角兽企业重金招聘Python工程师标准>>> 作者:MaggieDorami 我们来看这样一段程序: public static void main(String[] ar ...

  5. java中间缓存变量机制_Java中间缓存变量机制

    j++,是一个依赖于java里面的"中间缓存变量机制"来实现的, ++在前就是"先加后赋"(++j) ++在后就是"先赋后加"  (j++) ...

  6. Map实现java缓存机制的简单实例

    缓存是Java中主要的内容,主要目的是缓解项目访问数据库的压力以及提升访问数据的效率,以下是通过Map实现java缓存的功能,并没有用cache相关框架. 一.缓存管理类 CacheMgr.java ...

  7. linux 文件缓存大小设置,Linux文件读写机制及优化方式

    导读 Linux是一个可控性强的,安全高效的操作系统.本文只讨论Linux下文件的读写机制,不涉及不同读取方式如read,fread,cin等的对比,这些读取方式本质上都是调用系统api read,只 ...

  8. 剑指offer之java缓存总结,从单机缓存到分布式缓存架构

    1.缓存定义 高速数据存储层,提高程序性能 2.为什么要用缓存(读多写少,高并发) 1.提高读取吞吐量 2.提升应用程序性能 3.降低数据库成本 4.减少后端负载 5.消除数据库热点 6.可预测的性能 ...

  9. 【转载】Java JVM 运行机制及基本原理

    原博地址:https://zhuanlan.zhihu.com/p/25713880 JVM的基础概念 JVM的中文名称叫Java虚拟机,它是由软件技术模拟出计算机运行的一个虚拟的计算机. JVM也充 ...

最新文章

  1. X86嵌入式主板在IOT网关产品的应用
  2. (20):数据的准备工作
  3. 2、Eternal框架-svn_有更新!
  4. photoshop cc_如何使用Photoshop CC将图片变成卡通
  5. 蔚来测开提前批面试(一面)
  6. 如何启用×××服务器端的IPsec功能
  7. Android -- Property Animation
  8. 红帽linux进阶,Linux进阶第二天
  9. FISCO BCOS源码(4)第三方依赖和模块
  10. java实现文件移动_java实现文件的复制,移动
  11. CRC码计算及校验原理计算
  12. 用R语言下载任意地区DEM数据
  13. Linux学习—编译
  14. WPF教程二:理解WPF的布局系统和常用的Panel布局
  15. 酷!有人把火星车都造出来了,教程全面开源
  16. Linux终端更改字体
  17. 云计算与大数据概论第十一周(2)
  18. python加权最小二乘_如何计算加权最小二乘法的样本权重?
  19. adb模拟按键home_adb命令中 模拟按键 KeyCode 部分详解
  20. 【UnityShader】光线追踪体积光

热门文章

  1. 020-Json结构数据序列化异步传递
  2. mysql 分表的3种方法
  3. iOS开发那些事-表视图UI设计模式
  4. 父窗口与子窗口的数据传递问题
  5. 用window.open在同一个新窗口中访问指定url【IE页面缓存问题】
  6. php判断日期为空值,php判断值是否为空实例代码
  7. acwing算法题--多重背包问题一
  8. c语言对空指针memcpy,C语言memcpy 断错误
  9. python主函数的作用_python中main函数的用法
  10. Shell命令-搜索文件或目录之whereis、locate