可重入锁和不可重入锁
轉(zhuǎn)自https://www.cnblogs.com/dj3839/p/6580765.html
鎖的簡(jiǎn)單應(yīng)用
用lock來保證原子性(this.count++這段代碼稱為臨界區(qū))
什么是原子性,就是不可分,從頭執(zhí)行到尾,不能被其他線程同時(shí)執(zhí)行。
可通過CAS來實(shí)現(xiàn)原子操作
CAS(Compare and Swap):
CAS操作需要輸入兩個(gè)數(shù)值,一個(gè)舊值(期望操作前的值)和一個(gè)新值,在操作期間先比較下舊值有沒有發(fā)生變化,如果沒有發(fā)生變化,才交換成新值,發(fā)生了變化則不交換。
CAS主要通過compareAndSwapXXX()方法來實(shí)現(xiàn),而這個(gè)方法的實(shí)現(xiàn)需要涉及底層的unsafe類
unsafe類:java不能直接訪問操作系統(tǒng)底層,而是通過本地方法來訪問。Unsafe類提供了硬件級(jí)別的原子操作
這里有個(gè)介紹原子操作的博客
https://my.oschina.net/xinxingegeya/blog/499223
還有對(duì)unsafe類詳解的博客
http://www.cnblogs.com/mickole/articles/3757278.html
?
1 public class Counter{2 private Lock lock = new Lock();3 private int count = 0;4 public int inc(){5 lock.lock();6 this.count++;7 lock.unlock();8 return count;9 } 10 }不可重入鎖
先來設(shè)計(jì)一種鎖
1 public class Lock{2 private boolean isLocked = false;3 public synchronized void lock() throws InterruptedException{4 while(isLocked){ 5 wait();6 }7 isLocked = true;8 }9 public synchronized void unlock(){ 10 isLocked = false; 11 notify(); 12 } 13 }這其實(shí)是個(gè)不可重入鎖,舉個(gè)例子
1 public class Count{2 Lock lock = new Lock();3 public void print(){4 lock.lock();5 doAdd();6 lock.unlock();7 }8 public void doAdd(){9 lock.lock(); 10 //do something 11 lock.unlock(); 12 } 13 }當(dāng)調(diào)用print()方法時(shí),獲得了鎖,這時(shí)就無法再調(diào)用doAdd()方法,這時(shí)必須先釋放鎖才能調(diào)用,所以稱這種鎖為不可重入鎖,也叫自旋鎖。
可重入鎖
設(shè)計(jì)如下:
1 public class Lock{2 boolean isLocked = false;3 Thread lockedBy = null;4 int lockedCount = 0;5 public synchronized void lock()6 throws InterruptedException{7 Thread thread = Thread.currentThread();8 while(isLocked && lockedBy != thread){9 wait(); 10 } 11 isLocked = true; 12 lockedCount++; 13 lockedBy = thread; 14 } 15 public synchronized void unlock(){ 16 if(Thread.currentThread() == this.lockedBy){ 17 lockedCount--; 18 if(lockedCount == 0){ 19 isLocked = false; 20 notify(); 21 } 22 } 23 } 24 }相對(duì)來說,可重入就意味著:線程可以進(jìn)入任何一個(gè)它已經(jīng)擁有的鎖所同步著的代碼塊。
第一個(gè)線程執(zhí)行print()方法,得到了鎖,使lockedBy等于當(dāng)前線程,也就是說,執(zhí)行的這個(gè)方法的線程獲得了這個(gè)鎖,執(zhí)行add()方法時(shí),同樣要先獲得鎖,因不滿足while循環(huán)的條件,也就是不等待,繼續(xù)進(jìn)行,將此時(shí)的lockedCount變量,也就是當(dāng)前獲得鎖的數(shù)量加一,當(dāng)釋放了所有的鎖,才執(zhí)行notify()。如果在執(zhí)行這個(gè)方法時(shí),有第二個(gè)線程想要執(zhí)行這個(gè)方法,因?yàn)閘ockedBy不等于第二個(gè)線程,導(dǎo)致這個(gè)線程進(jìn)入了循環(huán),也就是等待,不斷執(zhí)行wait()方法。只有當(dāng)?shù)谝粋€(gè)線程釋放了所有的鎖,執(zhí)行了notify()方法,第二個(gè)線程才得以跳出循環(huán),繼續(xù)執(zhí)行。
這就是可重入鎖的特點(diǎn)。
java中常用的可重入鎖
synchronized
java.util.concurrent.locks.ReentrantLock
ps:順便記錄下java中實(shí)現(xiàn)原子操作的類(記錄至http://blog.csdn.net/huzhigenlaohu/article/details/51646455)
- AtomicIntegerFieldUpdater:原子更新整型的字段的更新器
- AtomicLongFieldUpdater:原子更新長(zhǎng)整型字段的更新器
- AtomicStampedReference:原子更新帶有版本號(hào)的引用類型。該類將整型數(shù)值與引用關(guān)聯(lián)起來,可用于原子的更新數(shù)據(jù)和數(shù)據(jù)的版本號(hào),可以解決使用CAS進(jìn)行原子更新時(shí)可能出現(xiàn)的ABA問題。
- AtomicReference :原子更新引用類型
- AtomicReferenceFieldUpdater :原子更新引用類型里的字段
- AtomicMarkableReference:原子更新帶有標(biāo)記位的引用類型。可以原子更新一個(gè)布爾類型的標(biāo)記位和應(yīng)用類型
- AtomicIntegerArray :原子更新整型數(shù)組里的元素
- AtomicLongArray :原子更新長(zhǎng)整型數(shù)組里的元素
- AtomicReferenceArray : 原子更新引用類型數(shù)組的元素
- AtomicBooleanArray :原子更新布爾類型數(shù)組的元素
- AtomicBoolean :原子更新布爾類型
- AtomicInteger: 原子更新整型
- AtomicLong: 原子更新長(zhǎng)整型
?
轉(zhuǎn)載于:https://www.cnblogs.com/ffaiss/p/11132984.html
總結(jié)
以上是生活随笔為你收集整理的可重入锁和不可重入锁的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 主管问我:你以为单元测试,只是测试吗?
- 下一篇: 求你了,别再随便打日志了,教你动态修改日