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

IF value in cached THEN

return value from cache

ELSE

compute value

save value in cache

return 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 - ) + getNumber(index - );

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 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 - ) + getNumber(index - );

cache.put(index, value);

return value;

}

//使用折返锁,使读写同步

public Long getNumberByLock(final long index) {

long value =;

try {

lock.lock();

if (cache.containsKey(index)) {

return cache.get(index);

}

value = getNumberByLock(index - ) + getNumberByLock(index - );

cache.put(index, value);

return value;

}

catch (Exception e)

{}

finally

{

lock.unlock();

}

return 0l;

}

//使用同步,使读写同步

public Long getNumberBySynchroniz(final long index) {

synchronized (o)

{

long value =;

try {

if (cache.containsKey(index)) {

return cache.get(index);

}

value = getNumberBySynchroniz(index - ) + getNumberBySynchroniz(index - );

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());

}

}

,"Thread-A");

threadA.start();

final Thread threadB = new Thread(new Runnable() {

public void run() {

System.out.println(naiveCacheExample.getNumberBySynchroniz());

}

}, "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 {

private final ConcurrentMap> cache = new ConcurrentHashMap<>();

private Future createFutureIfAbsent(final K key, final Callable callable) {

Future future = cache.get(key);

if (future == null) {

final FutureTask futureTask = new FutureTask(callable);

future = cache.putIfAbsent(key, futureTask);

if (future == null) {

future = futureTask;

futureTask.run();

}

}

return future;

}

public V getValue(final K key, final Callable callable) throws InterruptedException, ExecutionException {

try {

final Future 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() {

@Override

public V call() throws Exception {

return value;

}

});

}

}

参考博客:

http://www.javacreed.com/how-to-cache-results-to-boost-performance/

Java缓存机制

1 Java缓存 1.1 jvm内置缓存 Java中实现缓存的方式有很多,比如用static hashMap基于内存缓存的jvm内置缓存,简单不实用,保对象的有效性和周期无法控制,容易造成内存急剧上升 ...

Java的多线程机制系列:&lpar;二)缓存一致性和CAS

一.总线锁定和缓存一致性 这是两个操作系统层面的概念.随着多核时代的到来,并发操作已经成了很正常的现象,操作系统必须要有一些机制和原语,以保证某些基本操作的原子性.首先处理器需要保证读一个字节或写一个 ...

并发读写缓存实现机制&lpar;一&rpar;:为什么ConcurrentHashMap可以这么快?

大家都知道ConcurrentHashMap的并发读写速度很快,但为什么它会这么快?这主要归功于其内部数据结构和独特的hash运算以及分离锁的机制.做游戏性能很重要,为了提高数据的读写速度,方法之一就 ...

Java缓存学习之二:浏览器缓存机制

浏览器端的九种缓存机制介绍 浏览器缓存是浏览器端保存数据用于快速读取或避免重复资源请求的优化机制,有效的缓存使用可以避免重复的网络请求和浏览器快速地读取本地数据,整体上加速网页展示给用户.浏览器端缓存 ...

利用java反射机制编写solr通用的java客户端

一.前言 通过上一篇的讲解,我们知道了dynamicFiled字段,它是动态的,不需要显示的声明.而且一些常用的基本类型solr已经默认给我们创建好了. 例如:*_i,*_is,等. 如果我们要使用动 ...

java并发之线程同步(synchronized和锁机制)

使用synchronized实现同步方法 使用非依赖属性实现同步 在同步块中使用条件(wait(),notify(),notifyAll()) 使用锁实现同步 使用读写锁实现同步数据访问 修改锁的公平 ...

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

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

Java的多线程机制系列:不得不提的volatile及指令重排序&lpar;happen-before&rpar;

一.不得不提的volatile volatile是个很老的关键字,几乎伴随着JDK的诞生而诞生,我们都知道这个关键字,但又不太清楚什么时候会使用它:我们在JDK及开源框架中随处可见这个关键字,但并发专 ...

Java线程新特征——Java并发库

一.线程池   Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利.为了编写高效稳定 ...

随机推荐

深入理解javascript原型和闭包(11)——执行上下文栈

继续上文的内容. 执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境.当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境.处于活动状态的执行 ...

JSON跨域请求

原理:首先客户机会注册一个callback,在发送跨域请求之前,会在url后附带注册的callback参数(如:callback1982342322),随后服务器拿到了callback参数,获取数据后 ...

详解Win2003 IIS6&period;0 301重定向带参数的问题(转摘)

网站更换域名,把旧域名用301指到新域名来. 从iis中设置url永久转向就可以,看上去很容易,用了一会儿才发现,参数都没有带上. 从微软网站上找到如下说明,果然好使: 重定向参考 (IIS 6. ...

&lpar;转&rpar;Server Tomcat v6&period;0 Server at localhost was unable to start within 45 seconds

仰天长啸 Server Tomcat v6.0 Server at localhost was unable to start within 45 seconds... 当启动tomcat时候出现 S ...

jQuery extend函数详解

一 jQuery的扩展方法原型是 $.extend(dest,src1,src2,src3); 含义是将src1,src2,src3合并到dest中,返回值为合并后的dest,该方法合并后,dest的 ...

201771010141 周强《面向对象设计 java》第十五周实验总结

理论部分 ◼ JAR文件◼ 应用程序首选项存储◼ Java Web Start JAR文件: 1.Java程序的打包:程序编译完成后,程序员将.class文件压缩打包为.jar文件后,GUI界面程序就 ...

Modbus通信协议 【 初识 Modbus】

Modbus协议     Modbus 协议是应用于电子控制器上的一种通用语言.通过此协议,控制器相互之间.控制器经由网络(例如以太网)和其它设备之间可以通信.它已经成为一通用工业标准.有了它,不同厂 ...

【逆向知识】裸函数&lpar;Naked函数&rpar;

1 说明 指定裸函数编写的函数,编译器生成不带任何多余代码. 利用此功能,可以使用内联汇编程序代码编写自己的 prolog/epilog 代码序列. 裸函数对于编写虚拟设备驱动程序特别有用. 2 练习 ...

Windows XP Manifest in Delphi

Find out how you can include the manifest into a Delphi project to allow your application to share t ...

关于CentOS 6下Hadoop占用系统态CPU高的处理办法【转】

一次不经意发现Hadoop的系统态CPU使用率很高,然后百度一下居然是个已知问题. RHEL6优化了内存申请的效率,而且在某些场景下对KVM的性能有明显提升:http://www.Linux-kvm. ...

写缓存java,编写线程安全的Java缓存读写机制 (原创)相关推荐

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

    一种习以为常的缓存写法: IF value in cached THENreturn value from cache ELSEcompute valuesave value in cacheretu ...

  2. 怎样用java编写日志_用JAVA写一个日志类程序以供大家学习

    中华网络安全联盟    作者:jacoo    来源:本站原创    时间:2006-4-18 说明: 尽管JAVA类库和其他工具提供了不少的纪录程序运行状态的日志类,我发觉也 不是万能的,有时需要根 ...

  3. java搭建线程池框架,JAVA线程池管理及分布式HADOOP调度框架搭建

    大家看到了线程的好处了吧!单线程需要10S,10个线程只需要1S.充分利用了系统资源实现并行计算.也许这里会产生一个误解,是不是增加的线程个数越多效率越高.线程越多处理性能越高这个是错误的,范式都要合 ...

  4. java编写家庭收支记录,Java家庭收支记账小项目(java基础)

    Java家庭收支记账小项目(java基础) Java家庭收支记账小项目(java基础) 需求说明: 模拟实现基于文本界面的<家庭记账软件>. 该软件能够记录家庭的收入.支出,并能够打印收支 ...

  5. java编写存钱_用Java编写一个简单的存款

    package desposit.money; public class DespositMoney { public static void main(String[] args) { Custom ...

  6. java编写管理系统_用java编写学生信息管理系统

    <用java编写学生信息管理系统>由会员分享,可在线阅读,更多相关<用java编写学生信息管理系统(7页珍藏版)>请在人人文库网上搜索. 1.用java编写学生信息管理系统im ...

  7. 用java编写圆锥_求java大神帮忙 求大神帮助!Java

    导航:网站首页 > 求java大神帮忙 求大神帮助!Java 求java大神帮忙 求大神帮助!Java 相关问题: 匿名网友: 普通类 public class CircularA { //求圆 ...

  8. java 编写方法和属性,Java类属性及方法的定义

    文章导读 [在定义类时,经常需要抽象出它的属性和方法,并定义在类的主体中.本文探讨Java类属性及方法的定义和使用.通过本文的学习,你将了解如何在Java类中定义属性及方法.属性值的设置和获取.] 本 ...

  9. java最高线程优先级是,Java线程优先级

    详细内容 Java 线程优先级 Thread 类中,使用如下属性来代表优先级.private int priority; 我们可以通过 setPriority(int newPriority) 来设置 ...

最新文章

  1. Pytorch实践中的几个重要概念
  2. 记住:永远不要在MySQL中使用“utf8”,请使用“utf8mb4” 程序员
  3. 风控建模:催收评分卡(四)--变量整理除了跟数据获取相关外还跟什么有关系?
  4. In this way, Wang Xing became Li Xiang’s closest
  5. 强悍的 vim —— 可视模式(visual mode)
  6. PHP json_decode($json, TRUE) TRUE使数据格式化为Array,而非object
  7. LeetCode之 x 的平方根
  8. git 修改密码_在windows中利用gitblit搭建git服务端
  9. 软件工程的极端所有权
  10. aws篇12 搭建一个推流、读流、RTSP服务器
  11. java xml解析 jdom_Java语言中XML的JDom解析方式
  12. openstack版本_庆祝新版本,供应商自由以及更多OpenStack新闻
  13. Extended VINS-Mono: 大规模户外环境进行绝对和相对车辆定位的系统性方法(IROS2021)...
  14. 教程|教你如何给你的头像添加一个好看的国旗
  15. python假设检验和区间估计_用 Python 实现常用的假设检验
  16. 阿古斯机器人_7.3.2暗牧神器燃烧王座语音
  17. magicyang语录
  18. 智慧社区的现状分析及发展前景
  19. LCD和OLED显示屏有什么区别?
  20. python中%s和%r的区别

热门文章

  1. RHEL6入门系列之十三,阶段练习1
  2. WPF快速指导5:验证
  3. IT新人如何快速成长
  4. HTML中label的两种使用方法
  5. Javascript类型转换的规则
  6. CSS属性display:inline-block使用揭秘
  7. Looper、Handler应用---实现主线程向子线程发送消息
  8. Android 自定义控件打造史上最简单的侧滑菜单
  9. 磁盘基准测试Bonnie++
  10. Makefile_02:程序的编译和链接