相信磁盤緩存在絕大部分的app上都有應用,相對於數據庫緩存來講,能夠不要注重於緩存的管理,比較開放和隨意。 再加上jakewharton早年間發佈的disklrucache框架,讓咱們使用磁盤緩存更加簡單,效率上和數據庫緩存也拉進了一步,之後有時間我在加上disklrucache的緩存解讀。java
可是在多線程的環境下,對同一份數據進行讀寫,會涉及到線程安全的問題。好比在一個線程讀取數據的時候,另一個線程在寫數據,而致使先後數據的不一致性;一個線程在寫數據的時候,另外一個線程也在寫,一樣也會致使線程先後看到的數據的不一致性。更嚴重的是一個線程在寫的時候,另外一個線程在讀。這裏的數據不一致是對於文件來講的,當文件裏的數據存儲的json時,殘缺的數據或者不完整的數據沒法生成對象,判斷沒有寫好甚至是報錯閃退。數據庫
使用Synchronized同步鎖保護線程安全,可是Synchronized存在明顯的一個性能問題就是讀與讀之間互斥,也就是說兩個線程的讀操做是順序執行的 下面給你們看下代碼方便理解json
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
read(Thread.currentThread());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
read(Thread.currentThread());
}
}).start();
}
public synchronized static void read(Thread thread){
System.out.println("開始運行時間:"+System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("結束運行時間:"+System.currentTimeMillis());
}
複製代碼
咱們來看一下運行結果,結論兩個兩個線程的讀操做是順序執行的,若是讀的次數多這個太影響性能了 緩存
最佳的方案通俗的來說應該是,能夠不少人同時讀,但不能同時寫,有人在寫的時候不能同時讀也不能同時寫,官方說法是讀和讀互不影響,讀和寫互斥,寫和寫互斥,好了接下來就是介紹今天的主角ReadWriteLock 讀寫鎖安全
ReadWriteLock是Java自帶的 所處位置 java.util.concurrent.locks,屬於java併發方案中的一種bash
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing
*/
Lock writeLock();
}
複製代碼
既然只是接口,那咱們真正要用的是實現了該接口的類 ReentrantReadWriteLock 可重入讀寫鎖多線程
可重入鎖,就是說一個線程在獲取某個鎖後,還能夠繼續獲取該鎖,即容許一個線程屢次獲取同一個鎖。通俗的來說就是支持在同一個線程裏面對多個文件進行讀寫操做,均可以獲取同一個鎖,可是獲取多少鎖就要回收多少鎖,下面給個例子方便理解併發
public static void main(String[] args) {
final ReadWriteLock lock = new ReentrantReadWriteLock();
lock.writeLock().lock();
lock.writeLock().lock();
new Thread(new Runnable() {
@Override
public void run() {
lock.writeLock().lock();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子線程運行");
lock.writeLock().unlock();
}
}).start();
System.out.println("主線程運行");
lock.writeLock().unlock();
// lock.writeLock().unlock(); 獲取兩次鎖,只釋放一次鎖
}
複製代碼
運行結果 app
注意:由於主線程2次獲取了鎖,可是卻只釋放1次鎖,形成死鎖,致使新線程永遠也不能獲取鎖。一個線程獲取多少次鎖,就必須釋放多少次鎖非公平模式(默認)框架
當以非公平初始化時,讀鎖和寫鎖的獲取的順序是不肯定的。非公平鎖主張競爭獲取,可能會延緩一個或多個讀或寫線程,可是會比公平鎖有更高的吞吐量。
公平模式
當以公平模式初始化時,線程將會以隊列的順序獲取鎖。當前線程釋放鎖後,等待時間最長的寫鎖線程就會被分配寫鎖;或者有一組讀線程組等待時間比寫線程長,那麼這組讀線程組將會被分配讀鎖。
源碼以下
public ReentrantReadWriteLock() {
this(false);
}
/**
* Creates a new {@code ReentrantReadWriteLock} with
* the given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
複製代碼
提供抽象類BaseCache的源碼,具體實現你們能夠經過本身的實際狀況去拓展
public abstract class BaseCache {
private final ReadWriteLock mLock = new ReentrantReadWriteLock();
/**
* 讀取緩存
*
* @param key 緩存key
* @param existTime 緩存時間
*/
final <T> T load(Type type, String key, long existTime) {
//1.先檢查key
Utils.checkNotNull(key, "key == null");
//2.判斷key是否存在,key不存在去讀緩存沒意義
if (!containsKey(key)) {
return null;
}
//3.判斷是否過時,過時自動清理
if (isExpiry(key, existTime)) {
remove(key);
return null;
}
//4.開始真正的讀取緩存
mLock.readLock().lock();
try {
// 讀取緩存
return doLoad(type, key);
} finally {
mLock.readLock().unlock();
}
}
/**
* 保存緩存
*
* @param key 緩存key
* @param value 緩存內容
* @return
*/
final <T> boolean save(String key, T value) {
//1.先檢查key
Utils.checkNotNull(key, "key == null");
//2.若是要保存的值爲空,則刪除
if (value == null) {
return remove(key);
}
//3.寫入緩存
boolean status = false;
mLock.writeLock().lock();
try {
status = doSave(key, value);
} finally {
mLock.writeLock().unlock();
}
return status;
}
/**
* 刪除緩存
*/
final boolean remove(String key) {
mLock.writeLock().lock();
try {
return doRemove(key);
} finally {
mLock.writeLock().unlock();
}
}
/**
* 獲取緩存大小
* @return
*/
long size() {
return getSize();
}
/**
* 清空緩存
*/
final boolean clear() {
mLock.writeLock().lock();
try {
return doClear();
} finally {
mLock.writeLock().unlock();
}
}
/**
* 是否包含 加final 是讓子類不能被重寫,只能使用doContainsKey
* 這裏加了鎖處理,操做安全。<br>
*
* @param key 緩存key
* @return 是否有緩存
*/
public final boolean containsKey(String key) {
mLock.readLock().lock();
try {
return doContainsKey(key);
} finally {
mLock.readLock().unlock();
}
}
/**
* 是否包含 採用protected修飾符 被子類修改
*/
protected abstract boolean doContainsKey(String key);
/**
* 是否過時
*/
protected abstract boolean isExpiry(String key, long existTime);
/**
* 讀取緩存
*/
protected abstract <T> T doLoad(Type type, String key);
/**
* 保存
*/
protected abstract <T> boolean doSave(String key, T value);
/**
* 刪除緩存
*/
protected abstract boolean doRemove(String key);
/**
* 清空緩存
*/
protected abstract boolean doClear();
/**
* 獲取緩存大小
*
* @return
*/
protected abstract long getSize();
}
複製代碼