写缓存java,编写线程安全的Java缓存读写机制 (原创)
一種習(xí)以為常的緩存寫法:
IF value in cached THEN
return value from cache
ELSE
compute value
save value in cache
return value
END IF
看上去邏輯無比正確,但實際上會造成2種問題:
1、這種方法是不線程安全的。
2、產(chǎn)生數(shù)值寫入重復(fù),造成錯誤的數(shù)據(jù)。
如下圖,在線程1執(zhí)行計算數(shù)值的過程中,線程2也進入數(shù)據(jù)檢查,將多次寫入數(shù)據(jù),程序非常危險。
演示錯誤代碼:
//最容易產(chǎn)生的錯誤寫法,先讀取緩存,讀不到就寫緩存
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、傳統(tǒng)的解決辦法,使用重入鎖 (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);
}
//最容易產(chǎn)生的錯誤寫法,先讀取緩存,讀不到就寫緩存
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內(nèi)置緩存 Java中實現(xiàn)緩存的方式有很多,比如用static hashMap基于內(nèi)存緩存的jvm內(nèi)置緩存,簡單不實用,保對象的有效性和周期無法控制,容易造成內(nèi)存急劇上升 ...
Java的多線程機制系列:(二)緩存一致性和CAS
一.總線鎖定和緩存一致性 這是兩個操作系統(tǒng)層面的概念.隨著多核時代的到來,并發(fā)操作已經(jīng)成了很正常的現(xiàn)象,操作系統(tǒng)必須要有一些機制和原語,以保證某些基本操作的原子性.首先處理器需要保證讀一個字節(jié)或?qū)懸粋€ ...
并發(fā)讀寫緩存實現(xiàn)機制(一):為什么ConcurrentHashMap可以這么快?
大家都知道ConcurrentHashMap的并發(fā)讀寫速度很快,但為什么它會這么快?這主要歸功于其內(nèi)部數(shù)據(jù)結(jié)構(gòu)和獨特的hash運算以及分離鎖的機制.做游戲性能很重要,為了提高數(shù)據(jù)的讀寫速度,方法之一就 ...
Java緩存學(xué)習(xí)之二:瀏覽器緩存機制
瀏覽器端的九種緩存機制介紹 瀏覽器緩存是瀏覽器端保存數(shù)據(jù)用于快速讀取或避免重復(fù)資源請求的優(yōu)化機制,有效的緩存使用可以避免重復(fù)的網(wǎng)絡(luò)請求和瀏覽器快速地讀取本地數(shù)據(jù),整體上加速網(wǎng)頁展示給用戶.瀏覽器端緩存 ...
利用java反射機制編寫solr通用的java客戶端
一.前言 通過上一篇的講解,我們知道了dynamicFiled字段,它是動態(tài)的,不需要顯示的聲明.而且一些常用的基本類型solr已經(jīng)默認(rèn)給我們創(chuàng)建好了. 例如:*_i,*_is,等. 如果我們要使用動 ...
java并發(fā)之線程同步(synchronized和鎖機制)
使用synchronized實現(xiàn)同步方法 使用非依賴屬性實現(xiàn)同步 在同步塊中使用條件(wait(),notify(),notifyAll()) 使用鎖實現(xiàn)同步 使用讀寫鎖實現(xiàn)同步數(shù)據(jù)訪問 修改鎖的公平 ...
Map實現(xiàn)java緩存機制的簡單實例
緩存是Java中主要的內(nèi)容,主要目的是緩解項目訪問數(shù)據(jù)庫的壓力以及提升訪問數(shù)據(jù)的效率,以下是通過Map實現(xiàn)java緩存的功能,并沒有用cache相關(guān)框架. 一.緩存管理類 CacheMgr.java ...
Java的多線程機制系列:不得不提的volatile及指令重排序(happen-before)
一.不得不提的volatile volatile是個很老的關(guān)鍵字,幾乎伴隨著JDK的誕生而誕生,我們都知道這個關(guān)鍵字,但又不太清楚什么時候會使用它:我們在JDK及開源框架中隨處可見這個關(guān)鍵字,但并發(fā)專 ...
Java線程新特征——Java并發(fā)庫
一.線程池 ??Sun在Java5中,對Java線程的類庫做了大量的擴展,其中線程池就是Java5的新特征之一,除了線程池之外,還有很多多線程相關(guān)的內(nèi)容,為多線程的編程帶來了極大便利.為了編寫高效穩(wěn)定 ...
隨機推薦
深入理解javascript原型和閉包(11)——執(zhí)行上下文棧
繼續(xù)上文的內(nèi)容. 執(zhí)行全局代碼時,會產(chǎn)生一個執(zhí)行上下文環(huán)境,每次調(diào)用函數(shù)都又會產(chǎn)生執(zhí)行上下文環(huán)境.當(dāng)函數(shù)調(diào)用完成時,這個上下文環(huán)境以及其中的數(shù)據(jù)都會被消除,再重新回到全局上下文環(huán)境.處于活動狀態(tài)的執(zhí)行 ...
JSON跨域請求
原理:首先客戶機會注冊一個callback,在發(fā)送跨域請求之前,會在url后附帶注冊的callback參數(shù)(如:callback1982342322),隨后服務(wù)器拿到了callback參數(shù),獲取數(shù)據(jù)后 ...
詳解Win2003 IIS6.0 301重定向帶參數(shù)的問題(轉(zhuǎn)摘)
網(wǎng)站更換域名,把舊域名用301指到新域名來. 從iis中設(shè)置url永久轉(zhuǎn)向就可以,看上去很容易,用了一會兒才發(fā)現(xiàn),參數(shù)都沒有帶上. 從微軟網(wǎng)站上找到如下說明,果然好使: 重定向參考 (IIS 6. ...
(轉(zhuǎn))Server Tomcat v6.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... 當(dāng)啟動tomcat時候出現(xiàn)?S ...
jQuery extend函數(shù)詳解
一 jQuery的擴展方法原型是 $.extend(dest,src1,src2,src3); 含義是將src1,src2,src3合并到dest中,返回值為合并后的dest,該方法合并后,dest的 ...
201771010141 周強《面向?qū)ο笤O(shè)計 java》第十五周實驗總結(jié)
理論部分 ? JAR文件? 應(yīng)用程序首選項存儲? Java Web Start JAR文件: 1.Java程序的打包:程序編譯完成后,程序員將.class文件壓縮打包為.jar文件后,GUI界面程序就 ...
Modbus通信協(xié)議 【 初識 Modbus】
Modbus協(xié)議 ? ? Modbus 協(xié)議是應(yīng)用于電子控制器上的一種通用語言.通過此協(xié)議,控制器相互之間.控制器經(jīng)由網(wǎng)絡(luò)(例如以太網(wǎng))和其它設(shè)備之間可以通信.它已經(jīng)成為一通用工業(yè)標(biāo)準(zhǔn).有了它,不同廠 ...
【逆向知識】裸函數(shù)(Naked函數(shù))
1 說明 指定裸函數(shù)編寫的函數(shù),編譯器生成不帶任何多余代碼. 利用此功能,可以使用內(nèi)聯(lián)匯編程序代碼編寫自己的 prolog/epilog 代碼序列. 裸函數(shù)對于編寫虛擬設(shè)備驅(qū)動程序特別有用. 2 練習(xí) ...
Windows XP Manifest in Delphi
Find out how you can include the manifest into a Delphi project to allow your application to share t ...
關(guān)于CentOS 6下Hadoop占用系統(tǒng)態(tài)CPU高的處理辦法【轉(zhuǎn)】
一次不經(jīng)意發(fā)現(xiàn)Hadoop的系統(tǒng)態(tài)CPU使用率很高,然后百度一下居然是個已知問題. RHEL6優(yōu)化了內(nèi)存申請的效率,而且在某些場景下對KVM的性能有明顯提升:http://www.Linux-kvm. ...
總結(jié)
以上是生活随笔為你收集整理的写缓存java,编写线程安全的Java缓存读写机制 (原创)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java jpanel方法,Java J
- 下一篇: php链接Access数据库代码,PHP