博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
编写线程安全的Java缓存读写机制 (原创)
阅读量:4135 次
发布时间:2019-05-25

本文共 4751 字,大约阅读时间需要 15 分钟。

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

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
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
{ 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/

转载地址:http://kxpvi.baihongyu.com/

你可能感兴趣的文章
禁止使用类的copy构造函数和赋值操作符
查看>>
C++学习路线
查看>>
私有构造函数
查看>>
组队总结
查看>>
TitledBorder 设置JPanel边框
查看>>
DBCP——开源组件 的使用
查看>>
抓包工具
查看>>
海量数据相似度计算之simhash和海明距离
查看>>
DeepLearning tutorial(5)CNN卷积神经网络应用于人脸识别(详细流程+代码实现)
查看>>
DeepLearning tutorial(6)易用的深度学习框架Keras简介
查看>>
DeepLearning tutorial(7)深度学习框架Keras的使用-进阶
查看>>
流形学习-高维数据的降维与可视化
查看>>
Python-OpenCV人脸检测(代码)
查看>>
python+opencv之视频人脸识别
查看>>
人脸识别(OpenCV+Python)
查看>>
6个强大的AngularJS扩展应用
查看>>
网站用户登录系统设计——jsGen实现版
查看>>
第三方SDK:讯飞语音听写
查看>>
第三方SDK:JPush SDK Eclipse
查看>>
第三方开源库:imageLoader的使用
查看>>