locks包結構層次 Lock 介面 方法簽名描述 void lock(); 獲取鎖(不死不休) boolean tryLock(); 獲取鎖(淺嘗輒止) boolean tryLock(long time, TimeUnit unit) throws InterruptedException; 獲 ...
locks包結構層次
Lock 介面
方法簽名 | 描述 |
---|---|
void lock(); | 獲取鎖(不死不休) |
boolean tryLock(); | 獲取鎖(淺嘗輒止) |
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; | 獲取鎖(過時不候) |
void lockInterruptibly() throws InterruptedException; | 獲取鎖(任人擺佈) |
void unlock(); | 釋放鎖 |
Condition newCondition(); |
代碼示例:
public class GetLockDemo {
// 公平鎖
// static Lock lock =new ReentrantLock(true);
// 非公平鎖
static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
// 主線程 拿到鎖
lock.lock();
Thread thread =
new Thread(
() -> {
// 子線程 獲取鎖(不死不休)
System.out.println("begain to get lock...");
lock.lock();
System.out.println("succeed to get lock...");
// // 子線程 獲取鎖(淺嘗輒止)
// boolean result = lock.tryLock();
// System.out.println("是否獲得到鎖:" + result);
//
// // 子線程 獲取鎖(過時不候)
// try {
// boolean result1 = lock.tryLock(5, TimeUnit.SECONDS);
// System.out.println("是否獲得到鎖:" + result1);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// // 子線程 獲取鎖(任人擺佈)
// try {
// System.out.println("start to get lock Interruptibly");
// lock.lockInterruptibly();
// } catch (InterruptedException e) {
// e.printStackTrace();
// System.out.println("dad asked me to stop...");
// }
});
thread.start();
Thread.sleep(10000L);
lock.unlock();
}
}
結論:
- lock() 最常用
- lockInterruptibly() 方法一般更昂貴,有的實現類可能沒有實現 lockInterruptible() 方法。只有真的需要用中斷時,才使用,使用前應看清實現類對該方法的描述。
Condition
Object中的wait(), notify(), notifyAll()方法是和synchronized配合使用的可以喚醒一個或者多個線程。Condition是需要與Lock配合使用的,提供多個等待集合和更精確的控制(底層是park/unpark機制);
協作方式 | 死鎖方式1 (鎖) | 死鎖方式2(先喚醒,再掛起) | 備註 |
---|---|---|---|
suspend/resume | 死鎖 | 死鎖 | 棄用 |
wait/notify | 不死鎖 | 死鎖 | 只用於synchronized關鍵字 |
park/unpark | 死鎖 | 不死鎖 | |
condition | 不死鎖 | 死鎖 |
condition代碼示例:
public class ConditionDemo {
static Lock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
Thread thread =
new Thread(
() -> {
lock.lock();
System.out.println("condition.await()");
try {
condition.await();
System.out.println("here i am...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
thread.start();
Thread.sleep(2000L);
lock.lock();
condition.signalAll();
lock.unlock();
}
}
ReetrantLock
ReentrantLock是可重入鎖,同一線程可以多次獲取到鎖
ReentrantLock實現原理分析
- ReentrantLock需要一個owner用來標記那個線程獲取到了鎖,一個count用來記錄加鎖的次數和一個waiters等待隊列用來存放沒有搶到鎖的線程列表
- 當有線程進來時,會先判斷count的值,如果count為0說明鎖沒有被占用
- 然後通過CAS操作進行搶鎖
- 如果搶到鎖則count的值會加1,同時將owner設置為當前線程的引用
- 如果count不為0同時owner指向當前線程的引用,則將count的值加1
- 如果count不為0同時owner指向的不是當前線程的引用,則將線程放入等待隊列waiters中
- 如果CAS搶鎖失敗,則將線程放入等待隊列waiters中
- 當線程使用完鎖後,會釋放其持有的鎖,釋放鎖時會將count的值減1,如果count值為0則將owner設為null
- 如果count值不為0則會喚醒等待隊列頭部的線程進行搶鎖
手動實現ReentrantLock代碼示例:
public class MyReentrantLock implements Lock {
// 標記重入次數的count值
private AtomicInteger count = new AtomicInteger(0);
// 鎖的擁有者
private AtomicReference<Thread> owner = new AtomicReference<>();
// 等待隊列
private LinkedBlockingDeque<Thread> waiters = new LinkedBlockingDeque<>();
@Override
public boolean tryLock() {
// 判斷count是否為0,若count!=0,說明鎖被占用
int ct = count.get();
if (ct != 0) {
// 判斷鎖是否被當前線程占用,若被當前線程占用,做重入操作,count+=1
if (owner.get() == Thread.currentThread()) {
count.set(ct + 1);
return true;
} else {
// 若不是當前線程占用,互斥,搶鎖失敗,return false
return false;
}
} else {
// 若count=0, 說明鎖未被占用,通過CAS(0,1) 來搶鎖
if (count.compareAndSet(ct, ct + 1)) {
// 若搶鎖成功,設置owner為當前線程的引用
owner.set(Thread.currentThread());
return true;
} else {
return false;
}
}
}
@Override
public void lock() {
// 嘗試搶鎖
if (!tryLock()) {
// 如果失敗,進入等待隊列
waiters.offer(Thread.currentThread());
// 自旋
for (; ; ) {
// 判斷是否是隊列頭部,如果是
Thread head = waiters.peek();
if (head == Thread.currentThread()) {
// 再次嘗試搶鎖
if (!tryLock()) {
// 若搶鎖失敗,掛起線程,繼續等待
LockSupport.park();
} else {
// 若成功,就出隊列
waiters.poll();
return;
}
} else {
// 如果不是隊列頭部,就掛起線程
LockSupport.park();
}
}
}
}
public boolean tryUnlock() {
// 判斷,是否是當前線程占有鎖,若不是,拋異常
if (owner.get() != Thread.currentThread()) {
throw new IllegalMonitorStateException();
} else {
// 如果是,就將count-1 若count變為0 ,則解鎖成功
int ct = count.get();
int nextc = ct - 1;
count.set(nextc);
// 判斷count值是否為0
if (nextc == 0) {
owner.compareAndSet(Thread.currentThread(), null);
return true;
} else {
return false;
}
}
}
@Override
public void unlock() {
// 嘗試釋放鎖
if (tryUnlock()) {
// 獲取隊列頭部, 如果不為null則將其喚醒
Thread thread = waiters.peek();
if (thread != null) {
LockSupport.unpark(thread);
}
}
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public void lockInterruptibly() throws InterruptedException {}
@Override
public Condition newCondition() {
return null;
}
}
synchronized VS Lock
synchronized:
優點:
- 使用簡單,語義清晰,哪裡需要點哪裡
- 由JVM提供,提供了多種優化方案(鎖粗化,鎖消除,偏向鎖,輕量級鎖)
- 鎖的釋放由虛擬機完成,不用人工干預,降低了死鎖的可能性
缺點:悲觀的排他鎖,無法實現鎖的高級功能如公平鎖,讀寫鎖等
Lock:
優點:可以實現synchronized無法實現的鎖的高級功能如公平鎖,讀寫鎖等,同時還可以實現更多的功能
缺點:需手動釋放鎖unlock,使用不當容易造成死鎖
結論: 兩者都是可重入鎖,synchronized可以類比為傻瓜相機,提供了固定的功能,而Lock可以類比為單方,可以根據需要調節所需的功能
更多相關文章
-
Python提供了切片(Slice)操作符:可以一次取出多個列表元素 L[0:3]表示,從索引0開始取,直到索引3為止,但不包括索引3。0可以省略:L[:3] L[:]:就是整個列表 補充: 前10個數,每兩個取一個: >>> L[:10:2] [0, 2, 4, 6, 8] s[:2:-1]表示從 ...
-
DDL、DML、DCL、DQL的簡單操作,從溫習中學到之前沒有體會到的一些知識,在忙碌中偷得一日閑。 ...
-
目錄內容 DOS命令 電腦配置 Java語言的特性 Java兩種核心機制 Java語言環境搭建 第一個Java程式 註釋 Java語句說明 編程風格 作業 DOS命令 電腦配置 Java語言的特性 Java兩種核心機制 Java語言環境搭建 第一個Java程式 註釋 Java語句說明 編程風格 作 ...
-
program ex implicit none character(len=40) A(3000),B(3000),C(3000) !A異常、B已開挖、C需標記 integer i,j,N1,N2,count !N1是10號文件行數,N2是11號文件行數,count是計數器 open(unit=1... ...
-
9.11 進程池與線程池 池子使用來限制併發的任務數目,限制我們的電腦在一個自己可承受的範圍內去併發地執行任務 池子內什麼時候裝進程:併發的任務屬於計算密集型 池子內什麼時候裝線程:併發的任務屬於IO密集型 進程池: 線程池: 9.112 基於多線程實現併發的套接字通信(使用線程池) 服務端: f ...
-
del是個語句而不是方法 del member[1]:通過下標進行刪除 del member:也可刪除整個列表 remove():根據列表元素本身來刪除,而不是通過下標 member.remove('天天') pop(): member.pop():預設拋出列表最後一個元素, name = memb ...
-
問題描述 近來,跳一跳這款小游戲風靡全國,受到不少玩家的喜愛。 簡化後的跳一跳規則如下:玩家每次從當前方塊跳到下一個方塊,如果沒有跳到下一個方塊上則游戲結束。 如果跳到了方塊上,但沒有跳到方塊的中心則獲得1分;跳到方塊中心時,若上一次的得分為1分或這是本局游戲的第一次跳躍則此次得分為2分,否則此次得 ...
-
JVM中優化指南 如何將新對象預留在年輕代 如何讓大對象進入年老代 如何設置對象進入年老代的年齡 穩定的 Java 堆 VS 動蕩的 Java 堆 增大吞吐量提升系統性能 嘗試使用大的記憶體分頁 使用非占有的垃圾回收器 Java虛擬機有自己完善的硬體架構,如處理器、堆棧、寄存器等,還具有相應的指令系統 ...