文章目錄
- 前言
- 一.synchronized的缺陷
- 二.Lock介面的特性
- 三.Lock介面基本的方法:
- 四、Lock介面的實作類:ReentrantLock
- 五、ReadWriteLock
- 六、Condition介面
- 簡介
- Condition介面的常見方法:
- . 使用單個Condition實體實作等待/通知機制:
- 使用多個Condition實體實作等待/通知機制:
- 使用Condition實作順序執行
- 七、ReentrantReadWriteLock
- 八、公平鎖與非公平鎖
- 九、可重入鎖
前言
本章內容并沒有特別之處,主要目的是為了整理自己學習筆記,作為一個鞏固和更深刻的認識,方便后面自己查閱,也供一些朋友參考
當然了本章的內容學習,需要先了解執行緒同步,執行緒鎖的內容后再學習,
也就是上一期的篇章,可供參考,
執行緒安全(一)synchronized 什么是執行緒同步?什么是執行緒安全?什么是執行緒鎖?synchronized怎么用?如何理解wait()和sleep()的區別?超詳細例程講解-------手摸手教會小白
一.synchronized的缺陷
上一章我們提到synchronized使用的幾個案例,它們特性是這樣的,當一個執行緒在執行帶鎖的方法時,其他需要使用這個方法的執行緒只能等待 ,等待獲取鎖的執行緒釋放鎖,
| 序號 | 獲取鎖的執行緒釋放鎖的兩種情況: |
|---|---|
| 1 | 獲取鎖的執行緒執行完了該代碼塊,然后執行緒釋放對鎖的占有. |
| 2 | 執行緒執行發生例外,此時JVM會讓執行緒自動釋放鎖, |
如果獲取鎖的執行緒由于要等待IO或者其他原因(比如呼叫sleep方法)被阻塞了,但是又沒有釋放鎖,其他執行緒便只能干巴巴地等待,這十分影響程式執行效率,因此就需要有一種機制可以不讓等待的執行緒一直無期限地等待下去(比如只等待一定的時間或者能夠回應中斷),通過Lock就可以辦到,通過Lock可以知道執行緒有沒有成功獲取到鎖,這個是synchronized無法辦到的,
總結一下:Lock提供了比synchronized更多的功能,但是要注意以下兩點:
(1)Lock不是Java語言內置的,synchronized是Java語言的關鍵字,因此是內置特性,Lock是一個類,通過這個類可以實作同步訪問;
(2)Lock和synchronized有一點非常大的不同,采用synchronized不需要用戶去手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完之后,系統會自動讓執行緒釋放對鎖的占用;而Lock則必須要用戶去手動釋放鎖,如果沒有主動釋放鎖,就有可能導致出現死鎖現象,
二.Lock介面的特性
| 特性 | 描述 |
|---|---|
| 嘗試非阻塞地獲取鎖 | 當前執行緒嘗試獲取鎖,如果這一時刻鎖沒有被其他執行緒獲取到,則成功獲取并持有鎖 |
| 能被中斷地獲取鎖 | 獲取到鎖的執行緒能夠回應中斷,當獲取到鎖的執行緒被中斷時,中斷例外將會被拋出,同時鎖會被釋放 |
| 超時獲取鎖 | 在指定的截止時間之前獲取鎖, 超過截止時間后仍舊無法獲取則回傳 |
三.Lock介面基本的方法:
| 方法名稱 | 描述 |
|---|---|
| void lock() | 獲得鎖,如果鎖不可用,則當前執行緒將被禁用以進行執行緒調度,并處于休眠狀態,直到獲取鎖, |
| void lockInterruptibly() | 獲取鎖,如果可用并立即回傳,如果鎖不可用,那么當前執行緒將被禁用以進行執行緒調度,并且處于休眠狀態,和lock()方法不同的是在鎖的獲取中可以中斷當前執行緒(相應中斷, |
| Condition newCondition() | 獲取等待通知組件,該組件和當前的鎖系結,當前執行緒只有獲得了鎖,才能呼叫該組件的wait()方法,而呼叫后,當前執行緒將釋放鎖, |
| boolean tryLock() | 只有在呼叫時才可以獲得鎖,如果可用,則獲取鎖定,并立即回傳值為true;如果鎖不可用,則此方法將立即回傳值為false , |
| boolean tryLock(long time, TimeUnit unit) | 超時獲取鎖,當前執行緒在一下三種情況下會回傳: 1. 當前執行緒在超時時間內獲得了鎖;2.當前執行緒在超時時間內被中斷;3.超時時間結束,回傳false. |
| void unlock() | 釋放鎖, |
由于在前面講到如果采用Lock,必須主動去釋放鎖,并且在發生例外時,不會自動釋放鎖,因此一般來說,使用Lock必須在try{}catch{}塊中進行,并且將釋放鎖的操作放在finally塊中進行,以保證鎖一定被被釋放,防止死鎖的發生,通常使用Lock來進行同步的話,是以下面這種形式去使用的:
Lock lock = ...;
lock.lock();
try{
//處理任務
}catch(Exception ex){
}finally{
lock.unlock(); //釋放鎖
}
tryLock()方法是有回傳值的,它表示用來嘗試獲取鎖,如果獲取成功,則回傳true,如果獲取失敗(即鎖已被其他執行緒獲取),則回傳false,也就說這個方法無論如何都會立即回傳,在拿不到鎖時不會一直在那等待,
tryLock(long time, TimeUnit unit)方法和tryLock()方法是類似的,只不過區別在于這個方法在拿不到鎖時會等待一定的時間,在時間期限之內如果還拿不到鎖,就回傳false,如果如果一開始拿到鎖或者在等待期間內拿到了鎖,則回傳true,
所以,一般情況下通過tryLock來獲取鎖時是這樣使用的:
Lock lock = ...;
if(lock.tryLock()) {
try{
//處理任務
}catch(Exception ex){
}finally{
lock.unlock(); //釋放鎖
}
}else {
//如果不能獲取鎖,則直接做其他事情
}
lockInterruptibly()方法比較特殊,當通過這個方法去獲取鎖時,如果執行緒正在等待獲取鎖,則這個執行緒能夠回應中斷,即中斷執行緒的等待狀態,也就使說,當兩個執行緒同時通過lock.lockInterruptibly()想獲取某個鎖時,假若此時執行緒A獲取到了鎖,而執行緒B只有在等待,那么對執行緒B呼叫threadB.interrupt()方法能夠中斷執行緒B的等待程序,
由于lockInterruptibly()的宣告中拋出了例外,所以lock.lockInterruptibly()必須放在try塊中或者在呼叫lockInterruptibly()的方法外宣告拋出InterruptedException,
因此lockInterruptibly()一般的使用形式如下:
public void method() throws InterruptedException {
lock.lockInterruptibly();
try {
//.....
}
finally {
lock.unlock();
}
}
當一個執行緒獲取了鎖之后,是不會被interrupt()方法中斷的,
四、Lock介面的實作類:ReentrantLock
ReentrantLock,意思是“可重入鎖”,ReentrantLock是唯一實作了Lock介面的類,并且ReentrantLock提供了更多的方法,
ReentrantLock類常見方法:
構造方法:
| 方法名稱 | 描述 |
|---|---|
| ReentrantLock() | 創建一個 ReentrantLock的實體, |
| ReentrantLock(boolean fair) | 創建一個特定鎖型別(公平鎖/非公平鎖)的ReentrantLock的實體 |
ReentrantLock類常見方法(不包括介面中的方法):
| 方法名稱 | 描述 |
|---|---|
| int getHoldCount() | 查詢當前執行緒保持此鎖定的個數,也就是呼叫lock()方法的次數, |
| protected Thread getOwner() | 回傳當前擁有此鎖的執行緒,如果不擁有,則回傳 null |
| protected Collection getQueuedThreads() | 回傳包含可能正在等待獲取此鎖的執行緒的集合 |
| int getQueueLength() | 回傳等待獲取此鎖的執行緒數的估計, |
| protected Collection getWaitingThreads(Condition condition) | 回傳包含可能在與此鎖相關聯的給定條件下等待的執行緒的集合, |
| int getWaitQueueLength(Condition condition) | 回傳與此鎖相關聯的給定條件等待的執行緒數的估計, |
| boolean hasQueuedThread(Thread thread) | 查詢給定執行緒是否等待獲取此鎖, |
| boolean hasQueuedThreads() | 查詢是否有執行緒正在等待獲取此鎖, |
| boolean hasWaiters(Condition condition) | 查詢任何執行緒是否等待與此鎖相關聯的給定條件 |
| boolean isFair() | 如果此鎖的公平設定為true,則回傳 true , |
| boolean isHeldByCurrentThread() | 查詢此鎖是否由當前執行緒持有, |
| boolean isLocked() | 查詢此鎖是否由任何執行緒持有, |
五、ReadWriteLock
ReentrantLock(排他鎖)具有完全互斥排他的效果,即同一時刻只允許一個執行緒訪問,這樣做雖然雖然保證了實體變數的執行緒安全性,但效率非常低下,ReadWriteLock介面就是為了解決這個問題,
讀寫鎖維護了兩個鎖,一個是讀操作相關的鎖也成為共享鎖,一個是寫操作相關的鎖 也稱為排他鎖,通過分離讀鎖和寫鎖,其并發性比一般排他鎖有了很大提升,
ReadWriteLock介面,在它里面只定義了兩個方法:
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
一個用來獲取讀鎖,一個用來獲取寫鎖,也就是說將檔案的讀寫操作分開,分成2個鎖來分配給執行緒,從而使得多個執行緒可以同時進行讀操作,
六、Condition介面
簡介
synchronized關鍵字與 wait() 和 notify/notifyAll() 方法相結合可以實作 等待/通知機制,ReentrantLock類當然也可以實作,但是需要借助于Condition介面與newCondition() 方法,Condition是JDK1.5之后才有的,它具有很好的靈活性,比如可以實作多路通知功能也就是在一個Lock物件中可以創建多個Condition實體(即物件監視器),執行緒物件可以注冊在指定的Condition中,從而可以有選擇性的進行執行緒通知,在調度執行緒上更加靈活,
在使用notify/notifyAll()方法進行通知時,被通知的執行緒是由JVM選擇的,使用ReentrantLock類結合Condition實體可以實作“選擇性通知”,這個功能非常重要,而且是Condition介面默認提供的,
而synchronized關鍵字就相當于整個Lock物件中只有一個Condition實體,所有的執行緒都注冊在它一個身上,如果執行notifyAll()方法的話就會通知所有處于等待狀態的執行緒這樣會造成很大的效率問題,而Condition實體的signalAll()方法 只會喚醒注冊在該Condition實體中的所有等待執行緒# 五、Condition介面
Condition介面的常見方法:
| 方法名稱 | 描述 |
|---|---|
| void await() | 相當于Object類的wait方法 |
| boolean await(long time, TimeUnit unit) | 相當于Object類的wait(long timeout)方法 |
| signal() | 相當于Object類的notify方法 |
| signalAll() | 相當于Object類的notifyAll方法 |
. 使用單個Condition實體實作等待/通知機制:
public class UseSingleConditionWaitNotify {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.start();
Thread.sleep(3000);
service.signal();
}
static public class MyService {
private Lock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void await() {
lock.lock();
try {
System.out.println(" await時間為" + System.currentTimeMillis());
condition.await();
System.out.println("這是condition.await()方法之后的陳述句,condition.signal()方法之后我才被執行");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signal() throws InterruptedException {
lock.lock();
try {
System.out.println("signal時間為" + System.currentTimeMillis());
condition.signal();
Thread.sleep(3000);
System.out.println("這是condition.signal()方法之后的陳述句");
} finally {
lock.unlock();
}
}
}
static public class ThreadA extends Thread {
private MyService service;
public ThreadA(MyService service) {
super();
this.service = service;
}
@Override
public void run() {
service.await();
}
}
}
執行結果
await時間為1612080501270
signal時間為1612080504272
這是condition.signal()方法之后的陳述句
這是condition.await()方法之后的陳述句,condition.signal()方法之后我才被執行
可以看到,await()方法和signal()方法實作了與wait()、notify()同樣的功能,
注意: 必須在condition.await()方法呼叫之前呼叫lock.lock()代碼獲得同步監視器,不然會報錯,
使用多個Condition實體實作等待/通知機制:
public class UseMoreConditionWaitNotify {
public static void main(String[] args) throws InterruptedException {
MyserviceMoreCondition service = new MyserviceMoreCondition();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
Thread.sleep(3000);
service.signalAll_A();
}
static public class ThreadA extends Thread {
private MyserviceMoreCondition service;
public ThreadA(MyserviceMoreCondition service) {
super();
this.service = service;
}
@Override
public void run() {
service.awaitA();
}
}
static public class ThreadB extends Thread {
private MyserviceMoreCondition service;
public ThreadB(MyserviceMoreCondition service) {
super();
this.service = service;
}
@Override
public void run() {
service.awaitB();
}
}
}
MyserviceMoreCondition
public class MyserviceMoreCondition {
private Lock lock = new ReentrantLock();
public Condition conditionA = lock.newCondition();
public Condition conditionB = lock.newCondition();
public void awaitA() {
lock.lock();
try {
System.out.println("begin awaitA時間為" + System.currentTimeMillis()
+ " ThreadName=" + Thread.currentThread().getName());
conditionA.await();
System.out.println(" end awaitA時間為" + System.currentTimeMillis()
+ " ThreadName=" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void awaitB() {
lock.lock();
try {
System.out.println("begin awaitB時間為" + System.currentTimeMillis()
+ " ThreadName=" + Thread.currentThread().getName());
conditionB.await();
System.out.println(" end awaitB時間為" + System.currentTimeMillis()
+ " ThreadName=" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signalAll_A() {
lock.lock();
try {
System.out.println(" signalAll_A時間為" + System.currentTimeMillis()
+ " ThreadName=" + Thread.currentThread().getName());
conditionA.signalAll();
} finally {
lock.unlock();
}
}
public void signalAll_B() {
lock.lock();
try {
System.out.println(" signalAll_B時間為" + System.currentTimeMillis()
+ " ThreadName=" + Thread.currentThread().getName());
conditionB.signalAll();
} finally {
lock.unlock();
}
}
}
執行結果
begin awaitA時間為1612084608495 ThreadName=A
begin awaitB時間為1612084608512 ThreadName=B
signalAll_A時間為1612084611512 ThreadName=main
end awaitA時間為1612084611512 ThreadName=A
從結果上可以看出只有A執行緒被喚醒了,明顯的signalAll()與notifyAll()不一樣,它沒有喚醒全部wait()的執行緒,
使用Condition實作順序執行
執行緒的交叉呼叫可能產生死鎖,因此所有執行緒按順序使用鎖的話,是可以避免死鎖的,但是實際上不這么做,因為這需要知道所有需要排序的鎖,且對團隊協作開發帶來很大的難度,
public class ConditionSeqExec {
volatile private static int nextPrintWho = 1;
private static ReentrantLock lock = new ReentrantLock();
final private static Condition conditionA = lock.newCondition();
final private static Condition conditionB = lock.newCondition();
final private static Condition conditionC = lock.newCondition();
public static void main(String[] args) {
Thread threadA = new Thread() {
public void run() {
try {
lock.lock();
//如果nextPrintWho不為1就等待
while (nextPrintWho != 1) {
conditionA.await();
}
for (int i = 0; i < 3; i++) {
System.out.print("ThreadA " + (i + 1)+" " );
}
System.out.println();
nextPrintWho = 2;
//通知conditionB實體的執行緒運行
conditionB.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread threadB = new Thread() {
public void run() {
try {
lock.lock();
//如果nextPrintWho不為2就等待
while (nextPrintWho != 2) {
conditionB.await();
}
for (int i = 0; i < 3; i++) {
System.out.print("ThreadB " + (i + 1)+" " );
}
System.out.println();
nextPrintWho = 3;
//通知conditionC實體的執行緒運行
conditionC.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread threadC = new Thread() {
public void run() {
try {
lock.lock();
//如果nextPrintWho不為3就等待
while (nextPrintWho != 3) {
conditionC.await();
}
for (int i = 0; i < 3; i++) {
System.out.print("ThreadC " + (i + 1)+" " );
}
System.out.println();
nextPrintWho = 1;
//通知conditionA實體的執行緒運行
conditionA.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread[] aArray = new Thread[5];
Thread[] bArray = new Thread[5];
Thread[] cArray = new Thread[5];
for (int i = 0; i < 5; i++) {
aArray[i] = new Thread(threadA);
bArray[i] = new Thread(threadB);
cArray[i] = new Thread(threadC);
aArray[i].start();
bArray[i].start();
cArray[i].start();
}
}
}
運行結果
ThreadA 1 ThreadA 2 ThreadA 3
ThreadB 1 ThreadB 2 ThreadB 3
ThreadC 1 ThreadC 2 ThreadC 3
ThreadA 1 ThreadA 2 ThreadA 3
ThreadB 1 ThreadB 2 ThreadB 3
ThreadC 1 ThreadC 2 ThreadC 3
ThreadA 1 ThreadA 2 ThreadA 3
ThreadB 1 ThreadB 2 ThreadB 3
ThreadC 1 ThreadC 2 ThreadC 3
ThreadA 1 ThreadA 2 ThreadA 3
ThreadB 1 ThreadB 2 ThreadB 3
ThreadC 1 ThreadC 2 ThreadC 3
ThreadA 1 ThreadA 2 ThreadA 3
ThreadB 1 ThreadB 2 ThreadB 3
ThreadC 1 ThreadC 2 ThreadC 3
可以看到這樣就實作了,執行緒按A->B->C->A的順序依次執行
七、ReentrantReadWriteLock
ReentrantReadWriteLock實作了上面講的ReadWriteLock介面
ReentrantReadWriteLock的特性:
| 特性 | 說明 |
|---|---|
| 公平性選擇 | 支持非公平(默認)和公平的鎖獲取方式,吞吐量上來看還是非公平優于公平 |
| 重進入 | 該鎖支持重進入,以讀寫執行緒為例:讀執行緒在獲取了讀鎖之后,能夠再次獲取讀鎖,而寫執行緒在獲取了寫鎖之后能夠再次獲取寫鎖也能夠同時獲取讀鎖 |
| 鎖降級 | 遵循獲取寫鎖、獲取讀鎖再釋放寫鎖的次序,寫鎖能夠降級稱為讀鎖 |
讀讀共享
/**
* 兩個讀共享鎖
*/
public class ReadRead {
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public static void main(String[] args) {
final ReadRead test = new ReadRead();
new Thread(){
public void run() {
test.get(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.get(Thread.currentThread());
};
}.start();
}
public void get(Thread thread) {
rwl.readLock().lock();
try {
long start = System.currentTimeMillis();
while(System.currentTimeMillis() - start <= 1) {
System.out.println(thread.getName()+"正在進行讀操作");
}
System.out.println(thread.getName()+"讀操作完畢");
} finally {
rwl.readLock().unlock();
}
}
}
此時列印的結果為(每次結果不同):
Thread-0正在進行讀操作
Thread-0正在進行讀操作
Thread-0正在進行讀操作
Thread-0正在進行讀操作
Thread-0正在進行讀操作
Thread-0正在進行讀操作
Thread-0正在進行讀操作
Thread-0正在進行讀操作
Thread-0正在進行讀操作
Thread-1正在進行讀操作
Thread-0正在進行讀操作
Thread-1正在進行讀操作
Thread-1正在進行讀操作
Thread-0正在進行讀操作
Thread-0正在進行讀操作
Thread-0正在進行讀操作
Thread-0正在進行讀操作
Thread-0正在進行讀操作
Thread-1正在進行讀操作
Thread-1正在進行讀操作
Thread-1正在進行讀操作
Thread-1正在進行讀操作
Thread-1正在進行讀操作
Thread-1讀操作完畢
Thread-0正在進行讀操作
Thread-0讀操作完畢
說明thread1和thread2在同時進行讀操作,這樣就大大提升了讀操作的效率,
不過要注意的是,如果有一個執行緒已經占用了讀鎖,則此時其他執行緒如果要申請寫鎖,則申請寫鎖的執行緒會一直等待釋放讀鎖,
如果有一個執行緒已經占用了寫鎖,則此時其他執行緒如果申請寫鎖或者讀鎖,則申請的執行緒會一直等待釋放寫鎖,
// An highlighted block
var foo = 'bar';
// An highlighted block
var foo = 'bar';
// An highlighted block
var foo = 'bar';
八、公平鎖與非公平鎖
Lock鎖分為:公平鎖 和 非公平鎖,
公平鎖:表示執行緒獲取鎖的順序是按照執行緒加鎖的順序來分配的,即先來先得的FIFO先進先出順序,
非公平鎖:是隨機獲取鎖的,和公平鎖不一樣的就是先來的不一定先的到鎖,這樣可能造成某些執行緒一直拿不到鎖,結果也就是不公平的了,
/**
* 根據ReentrantLock創建時給定不同的引數
*/
public class FairorNofairLock {
public static void main(String[] args) throws InterruptedException {
final Service service = new Service(true);//true為公平鎖,false為非公平鎖
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("★執行緒" + Thread.currentThread().getName()
+ "運行了");
service.serviceMethod();
}
};
Thread[] threadArray = new Thread[10];
for (int i = 0; i < 10; i++) {
threadArray[i] = new Thread(runnable);
}
for (int i = 0; i < 10; i++) {
threadArray[i].start();
}
}
static public class Service {
private ReentrantLock lock;
public Service(boolean isFair) {
super();
lock = new ReentrantLock(isFair);
}
public void serviceMethod() {
lock.lock();
try {
System.out.println("ThreadName=" + Thread.currentThread().getName()
+ "獲得鎖定");
} finally {
lock.unlock();
}
}
}
}
使用公平鎖時的結果
★執行緒Thread-0運行了
★執行緒Thread-4運行了
ThreadName=Thread-0獲得鎖定
★執行緒Thread-1運行了
★執行緒Thread-2運行了
★執行緒Thread-3運行了
★執行緒Thread-7運行了
★執行緒Thread-9運行了
★執行緒Thread-8運行了
★執行緒Thread-6運行了
ThreadName=Thread-4獲得鎖定
ThreadName=Thread-1獲得鎖定
★執行緒Thread-5運行了
ThreadName=Thread-2獲得鎖定
ThreadName=Thread-3獲得鎖定
ThreadName=Thread-7獲得鎖定
ThreadName=Thread-9獲得鎖定
ThreadName=Thread-8獲得鎖定
ThreadName=Thread-6獲得鎖定
ThreadName=Thread-5獲得鎖定
可以看到執行緒開啟的順序時
0-4-1-2-3-7-9-8-6-5
獲得鎖的順序也是
0-4-1-2-3-7-9-8-6-5
使用非公平鎖時的結果
★執行緒Thread-0運行了
ThreadName=Thread-0獲得鎖定
★執行緒Thread-4運行了
★執行緒Thread-1運行了
ThreadName=Thread-4獲得鎖定
ThreadName=Thread-1獲得鎖定
★執行緒Thread-2運行了
★執行緒Thread-3運行了
ThreadName=Thread-2獲得鎖定
★執行緒Thread-6運行了
ThreadName=Thread-6獲得鎖定
★執行緒Thread-5運行了
ThreadName=Thread-5獲得鎖定
★執行緒Thread-9運行了
ThreadName=Thread-9獲得鎖定
ThreadName=Thread-3獲得鎖定
★執行緒Thread-7運行了
ThreadName=Thread-7獲得鎖定
★執行緒Thread-8運行了
ThreadName=Thread-8獲得鎖定
很明顯執行緒開啟的順序和獲得鎖定的順序不一致,
九、可重入鎖
如果鎖具備可重入性,則稱作為可重入鎖,像synchronized和ReentrantLock都是可重入鎖,可重入性實際上表明了鎖的分配機制:基于執行緒的分配,而不是基于方法呼叫的分配,舉個簡單的例子,當一個執行緒執行到某個synchronized方法時,比如說method1,而在method1中會呼叫另外一個synchronized方法method2,此時執行緒不必重新去申請鎖,而是可以直接執行方法method2,
class Demo{
public synchronized void method1() {
method2();
}
public synchronized void method2() {
//........
}
}
上述代碼中的兩個方法method1和method2都用synchronized修飾了,假如某一時刻,執行緒A執行到了method1,此時執行緒A獲取了這個物件的鎖,而由于method2也是synchronized方法,假如synchronized不具備可重入性,此時執行緒A需要重新申請鎖,但是這就會造成一個問題,因為執行緒A已經持有了該物件的鎖,而又在申請獲取該物件的鎖,這樣就會執行緒A一直等待永遠不會獲取到的鎖,
本章例程審核通過后將附上,
下一張將介紹一下樂觀鎖Atomic原理,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/255337.html
標籤:java
