Lock和synchronized同步塊一樣闷煤,是線程的同步機(jī)制检激,而且鎖是由synchronized同步塊的方式進(jìn)行實(shí)現(xiàn)的。
一個(gè)簡(jiǎn)單的鎖實(shí)現(xiàn)
public class Lock{
private boolean isLocked = false;
public synchronized void lock()
throws InterruptedException{
while(isLocked){
wait();
}
isLocked = true;
}
public synchronized void unlock(){
isLocked = false;
notify();
}
}
while(isLocked)循環(huán)痢站,也稱作旋轉(zhuǎn)鎖允蜈。旋轉(zhuǎn)鎖可以防止虛假喚醒冤吨。
鎖的可重入性
Java中的synchronized同步塊是可以重入的。也就是說線程可以進(jìn)入同一管程對(duì)象鎖同步的另一個(gè)java代碼塊陷寝。
上面的鎖實(shí)現(xiàn)是不可以重入的锅很,因?yàn)樯厦娴膶?shí)現(xiàn)中一個(gè)線程是否允許退出lock方法是有循環(huán)決定的,只有當(dāng)isLocked為false的時(shí)候凤跑,lock操作才被允許爆安。
public class Lock{
boolean isLocked = false;
Thread lockedBy = null;
int lockedCount = 0;
public synchronized void lock()
throws InterruptedException{
Thread callingThread = Thread.currentThread();
while(isLocked && lockedBy != callingThread){
wait();
}
isLocked = true;
lockedCount++;
lockedBy = callingThread;
}
public synchronized void unlock(){
if(Thread.curentThread() == this.lockedBy){
lockedCount--;
if(lockedCount == 0){
isLocked = false;
notify();
}
}
}
}
在這個(gè)lock改進(jìn)中,記錄了已鎖住該Lock實(shí)例的線程仔引。如果當(dāng)前鎖對(duì)象沒有被加鎖或者當(dāng)前調(diào)用線程已經(jīng)對(duì)該Lock實(shí)例加了鎖扔仓,那么就不會(huì)執(zhí)行wait方法。
另外還需要記錄同一線程重復(fù)對(duì)一個(gè)鎖對(duì)象加鎖的次數(shù)咖耘,否者調(diào)用一次unlock就會(huì)解除該對(duì)象的整個(gè)鎖翘簇。在unlock調(diào)用沒有到達(dá)時(shí)lock調(diào)用次數(shù)之前,我們不希望鎖被解除儿倒。
在finally語句中調(diào)用unlock()
使用Lock加鎖來保護(hù)臨界區(qū)版保,并且臨界區(qū)可能會(huì)拋出異常,那么久必須在finally語句中調(diào)用unlock()夫否。這樣可以保證該鎖對(duì)象可以被解鎖以便其他線程
能對(duì)其繼續(xù)加鎖彻犁。
讀寫鎖
多線程read 沒有write ==> 線程安全
一個(gè)或者多個(gè)線程write ==> 線程不安全
- Read Access
沒有線程在寫數(shù)據(jù)&&沒有線程請(qǐng)求寫數(shù)據(jù) - Write Access
沒有其他線程在讀或者寫
讀寫鎖的簡(jiǎn)單實(shí)現(xiàn):
- 如果沒有線程進(jìn)行寫請(qǐng)求或者寫操作的時(shí)候,可以獲取讀操作權(quán)限
- 如果沒有線程進(jìn)行有讀鎖且沒有線程有寫鎖的時(shí)候凰慈,可以獲取寫操作的權(quán)限
- 使用notifyAll喚醒線程汞幢,可以防止信號(hào)丟失
public class ReadWriteLock{
private int readers = 0;
private int writers = 0;
private int writeRequests = 0;
public synchronized void lockRead() throws InterruptedException{
while(writers > 0 || writeRequests > 0){
wait();
}
readers++;
}
public synchronized void unlockRead(){
readers--;
notifyAll();
}
public synchronized void lockWrite() throws InterruptedException{
writeRequests++;
while(readers > 0 || writers > 0){
wait();
}
writeRequests--;
writers++;
}
public synchronized void unlockWrite() throws InterruptedException{
writers--;
notifyAll();
}
}
實(shí)現(xiàn)可重入的讀寫鎖
可重入讀寫鎖的實(shí)現(xiàn):
- 讀鎖重入
讀鎖可重入,要么滿足獲取讀鎖的條件(沒有寫或者寫請(qǐng)求)微谓, 要么已經(jīng)持有讀鎖 - 寫鎖重入
一個(gè)線程已經(jīng)擁有寫鎖森篷,該線程允許重入
public class ReadWriteLock{
private Map<Thread, Integer> readingThreads =
new HashMap<Thread, Integer>();
private int writeAccesses = 0;
private int writeRequests = 0;
private Thread writingThread = null;
public synchronized void lockRead() throws InterruptedException{
Thread callingThread = Thread.currentThread();
while(!canGrantReadAccess(callingThread)){
wait();
}
// 獲取到讀鎖,記錄獲取讀鎖的次數(shù)
readingThreads.put(callingThread,
(getReadAccessCount(callingThread) + 1));
}
private boolean canGrantReadAccess(Thread callingThread){
// 當(dāng)前線程寫操作豺型,可以降級(jí)到讀操作
if( isWriter(callingThread) ) return true;
// 有寫操作仲智,不能獲取讀操作的鎖。如果當(dāng)前線程是讀操作姻氨,有寫等待钓辆,可以重入
if( hasWriter()) return false;
if( isReader(callingThread) ) return true;
if( hasWriteRequests()) return false;
return true;
}
public synchronized void unlockRead(){
Thread callingThread = Thread.currentThread();
if(!isReader(callingThread)){
throw new IllegalMonitorStateException("Calling Thread does not" +
" hold a read lock on this ReadWriteLock");
}
int accessCount = getReadAccessCount(callingThread);
if(accessCount == 1){ readingThreads.remove(callingThread); }
else { readingThreads.put(callingThread, (accessCount -1)); }
notifyAll();
}
public synchronized void lockWrite() throws InterruptedException{
writeRequests++;
Thread callingThread = Thread.currentThread();
while(! canGrantWriteAccess(callingThread)){
wait();
}
writeRequests--;
writeAccesses++;
writingThread = callingThread;
}
public synchronized void unlockWrite() throws InterruptedException{
if(!isWriter(Thread.currentThread()){
throw new IllegalMonitorStateException("Calling Thread does not" +
" hold the write lock on this ReadWriteLock");
}
writeAccesses--;
if(writeAccesses == 0){
writingThread = null;
}
notifyAll();
}
private boolean canGrantWriteAccess(Thread callingThread){
// 只有一個(gè)線程讀,可以升級(jí)為寫
if(isOnlyReader(callingThread)) return true;
// 有讀線程,阻塞岩馍;無寫線程,可以獲取寫鎖
if(hasReaders()) return false;
if(writingThread == null) return true;
// 有寫線程抖韩,且是當(dāng)前線程蛀恩,可重入
if(!isWriter(callingThread)) return false;
return true;
}
private int getReadAccessCount(Thread callingThread){
Integer accessCount = readingThreads.get(callingThread);
if(accessCount == null) return 0;
return accessCount.intValue();
}
private boolean hasReaders(){
return readingThreads.size() > 0;
}
private boolean isReader(Thread callingThread){
return readingThreads.get(callingThread) != null;
}
private boolean isOnlyReader(Thread callingThread){
return readingThreads.size() == 1 &&
readingThreads.get(callingThread) != null;
}
private boolean hasWriter(){
return writingThread != null;
}
private boolean isWriter(Thread callingThread){
return writingThread == callingThread;
}
private boolean hasWriteRequests(){
return this.writeRequests > 0;
}
}
重入鎖死
當(dāng)一個(gè)線程重新獲取鎖,該鎖是不可重入的茂浮,則可能導(dǎo)致重入鎖死双谆。
public class Reentrant{
public synchronized outer(){
inner();
}
public synchronized inner(){
//do something
}
}
避免重入鎖死的方法:
- 編寫代碼時(shí)避免再次獲取已經(jīng)持有的鎖
- 使用重入鎖