文章首發于個人部落格,歡迎通路關注:https://www.lin2j.tech
本文會簡單對比一下
synchronized
關鍵字和
Lock
的差別,不會講到底層原理。
synchronized
和
Lock
有什麼差別?使用Lock有什麼好處,舉例說明。
- 底層結構不同(所屬層面
vsJVM
)API
-
是關鍵字,屬于synchronized
層面的。JVM
-
(底層是通過monitorenter
對象來完成,其實monitor
和await
方法都依賴于notify
對象,monitor
和await
隻能再同步塊或者同步方法調用 )notify
-
monitorexit
-
-
是具體的類,屬于Lock
層面的鎖。API
- 使用方法不同(手動釋放和自動釋放的差別)
-
不需要手動釋放鎖,當synchronized
代碼執行完成以後,系統會自動讓線程釋放對所得占用。synchronized
-
則需要使用者去手動釋放鎖,如果沒有主動釋放鎖,就有可能導緻出現死鎖現象。需要ReentrantLock
和lock()
方法配合unlock()
語句塊來完成。try/finally
- 等待是否可中斷 (可否中斷)
-
不可中斷,除非抛異常或者正常運作完成。synchronized
-
可中斷:ReentrantLock
- 設定逾時方法
。tryLock(long timeout, TimeUnit unit)
-
放代碼塊中,調用lockInterruptibly()
方法可中斷。interrupt()
- 設定逾時方法
- 加鎖是否公平(公平鎖和非公平鎖)
-
是非公平鎖。Synchronized
-
兩者都可以,預設非公平鎖,構造方法可以傳入ReentrantLock
值,boolean
為公平鎖,true
為非公平鎖。false
- 鎖綁定多個條件(精确喚醒)
-
沒有Synchronized
-
用來實作分組喚醒需要喚醒的線程們,可以精确喚醒,而不是像ReentrantLock
,要麼随機喚醒一個線程,要麼喚醒全部線程。synchronized
方法。newCondition()
ReentrantLock
多條件精确控制線程 Demo
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author linjinjia [email protected]
* @date 2021/3/30 21:38
*/
public class SyncAndReentrantLockDemo {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(()->{
for (int i = 0; i < 3; i++) {
shareResource.print1();
}
}, "AA").start();
new Thread(()->{
for (int i = 0; i < 3; i++) {
shareResource.print2();
}
}, "BB").start();
new Thread(()->{
for (int i = 0; i < 3; i++) {
shareResource.print3();
}
}, "CC").start();
}
}
/**
* 多線程之間按照順序調用,實作 A->B->C三個線程啟動,要求如下:
* AA 列印 1 次,BB 列印 2次,CC列印 3次
* 來3輪
*/
class ShareResource {
/**
* flag = 1,線程AA啟動
* flag = 2,線程BB啟動
* flag = 3,線程CC啟動
*/
private int flag = 1;
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
public void print1(){
lock.lock();
try{
// 1. 判斷
while(flag != 1){
c1.await();
}
// 2. 幹活
print(Thread.currentThread().getName(), 1);
// 3. 通知
flag = 2;
c2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print2(){
lock.lock();
try{
// 1. 判斷
while(flag != 2){
c2.await();
}
// 2. 幹活
print(Thread.currentThread().getName(), 2);
// 3. 通知
flag = 3;
c3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print3(){
lock.lock();
try{
// 1. 判斷
while(flag != 3){
c3.await();
}
// 2. 幹活
print(Thread.currentThread().getName(), 3);
// 3. 通知
flag = 1;
c1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private void print(String name, int count){
for(int i = 0; i < count; i++){
System.out.println(name + "\t " + i);
}
}
}
輸出
AA 0
BB 0
BB 1
CC 0
CC 1
CC 2
AA 0
BB 0
BB 1
CC 0
CC 1
CC 2
AA 0
BB 0
BB 1
CC 0
CC 1
CC 2