日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java锁详解之ReentrantLock

發(fā)布時間:2025/3/20 java 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java锁详解之ReentrantLock 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.


文章目錄

      • 寫在前面
      • ReentrantLock的重要方法
      • ReentrantLock使用示例
      • ReentrantLock的公平和非公平鎖
      • ReentrantLock的重入鎖
      • ReentrantLock的Condition機制
      • ReentrantLock與synchronized(內部鎖)性能比較
      • Lock與synchronized區(qū)別

寫在前面

ReentrantLock繼承了AbstractQueuedSynchronizer(簡稱AQS),AQS用到了模板模式這種設計模式,所以閱讀AQS和ReentrantLock的源碼時,最好對模板設計模式有一定了解,這里我就講AQS了。

ReentrantLock是除了synchronized用得較多的一種鎖。ReentrantLock也屬于重入鎖,后面接著就會提到它的重入鎖實現(xiàn)原理。

ReentrantLock的功能要比內部鎖synchronized更多,如指定鎖等待時間的方法tryLock(long time,TimeUnit unit)、中斷鎖的方法lockInterruptibly()、沒獲取鎖直接返回的方法tryLock()。

所以,什么時候選擇ReentrantLock呢?

一般是synchronized 不滿足需求時,才選擇ReentrantLock,比如實現(xiàn)立即返回、可中斷、conditon機制時

ReentrantLock的重要方法

方法說明
lock()獲取鎖。如果鎖已經(jīng)被占用,則等待
tryLock()嘗試獲取鎖,拿到鎖返回true,沒拿到返回false,并立即返回
tryLock(long time, TimeUnit unit)在指定時間內會等待獲取鎖,如果一直拿不到就返回false,并立即返回。在等待過程中可以進行中斷
lockInterruptibley()獲取鎖。如果線程interrupted了,則跟著拋出異常中斷
unLock()釋放鎖
newCondition()創(chuàng)建一個與此 Lock 實例一起使用的 Condition 實例。

ReentrantLock使用示例

private ReentrantLock lock = new ReentrantLock();public void run() {// 加鎖 if(lock.tryLock()){System.out.println(Thread.currentThread().getName()+" 拿到鎖");try {Thread.sleep(3000);// doSomething...} catch (InterruptedException e) {e.printStackTrace();}finally {lock.unlock();// 釋放鎖}} else{System.out.println(Thread.currentThread().getName()+" 獲取鎖失敗");}}

ReentrantLock的公平和非公平鎖

公平鎖:公平鎖講究先來先到,線程在獲取鎖時,會先看這個鎖的等待隊列中是否有線程在等待,如果有,則當前線程就會直接進入等待隊列中,而不是直接去搶占鎖。

ReentrantLock fairLock = new ReentrantLock(true); // 初始化一個公平鎖

非公平鎖:不管是否有等待隊列,先直接嘗試獲取鎖,如果拿到鎖,則立刻占有鎖對象;如果未拿到,則自動排到隊尾等待。

ReentrantLock fairLock = new ReentrantLock(); // 初始化一個非公平鎖 ReentrantLock fairLock = new ReentrantLock(false); // 初始化一個非公平鎖

非公平鎖的性能比公平鎖的性能好很多,所以ReentrantLock默認是非公平鎖

為什么非公平鎖性能好?

//公平鎖靜態(tài)內部類 static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;final void lock() {acquire(1);}protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {// hasQueuedPredecessors返回true,說明當前節(jié)點不是頭節(jié)點或隊列不為空,此時直接加在隊列后面// hasQueuedPredecessors前面有個非符號,此時不再走&&后面的CAS操作。// hasQueuedPredecessors返回false,說明當前節(jié)點是頭節(jié)點或隊列為空,// 說明只有當前線程在競爭鎖,此時可以進行compareAndSetState操作。if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}}// 非公平鎖靜態(tài)內部類 static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;final void lock() {if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires); // 是Sync的方法}}// Sync的方法 final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {// 重入鎖機制int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false; }

看了ReentrantLock的公平鎖和非公平鎖的源代碼你就會發(fā)現(xiàn),公平鎖直接走的父類AbstractQueuedSynchronizer的acquire方法,而非公平鎖是先作CAS操作。非公平鎖這樣做的優(yōu)點是
1.如果直接拿到了鎖,就避免了維護node鏈表隊列
2.如果直接拿到了鎖,就避免了線程休眠和喚醒的上下文切換

ReentrantLock的重入鎖

ReentrantLock它是怎么實現(xiàn)重入鎖的呢?
截取前面小節(jié)的部分代碼:

else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}

從代碼可以看出,來獲取鎖的線程如果是當前占有鎖的線程,則直接將nextc+1。而且從if (nextc < 0)知道,可重入的次數(shù)是int的最大值。剛入門的同學可能不知道為什么會是int的最大的值,這是因為一個int值在做不限制累加,到了最大值2147483647時會溢出變成負數(shù),這個在大學計算機相關課程應該會講到。

ReentrantLock的Condition機制

ReentrantLock還提供了Condition機制來進行復雜的線程控制功能。

Condition是在java 1.5中才出現(xiàn)的,它用來替代傳統(tǒng)的Object的wait()、notify()實現(xiàn)線程間的協(xié)作,相比使用Object的wait()、notify(),使用Condition的await()、signal()這種方式實現(xiàn)線程間協(xié)作更加安全和高效。因此通常來說比較推薦使用Condition。

ArrayBlockingQueue的實現(xiàn)就依靠了Condition機制。如下核心代碼。

下面代碼中Condition機制的核心原理就是:當前線程被哪個condtion阻塞(調用await),就會加到當前condition的阻塞隊列里

import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock;/*** 數(shù)組阻塞隊列** @param <E>*/ class ArrayBlockingQueueDemon<E> {final ReentrantLock lock;/*** put元素時被阻塞的條件*/final Condition putCondition;/*** take數(shù)據(jù)時被阻塞的條件*/final Condition takeCondition;/*** 放元素的隊列*/final Object[] items;/*** take下一個元素的索引下標*/int takeIndex;/*** put下一個元素的索引下標*/int putIndex;/*** 隊列中元素個數(shù)*/int count;/*** 構造方法** @param capacity 允許隊列* @param fair 是否創(chuàng)建公平鎖*/public ArrayBlockingQueueDemon(int capacity, boolean fair) {if (capacity <= 0) {throw new IllegalArgumentException();}this.items = new Object[capacity];lock = new ReentrantLock(fair);takeCondition = lock.newCondition();putCondition = lock.newCondition();}public void put(E e) throws InterruptedException {if (e == null) {throw new NullPointerException();}lock.lockInterruptibly();try {// 隊列到達初始化上限時,不再允許向隊列放數(shù)據(jù),放數(shù)據(jù)的線程要等待// 此刻,當前線程會被添加到putCondition的阻塞隊列里while (count == items.length) {putCondition.await();}// 入隊enqueue(e);} finally {lock.unlock();}}public E take() throws InterruptedException {lock.lockInterruptibly();try {// 當隊列沒有數(shù)據(jù)時,拿數(shù)據(jù)的線程等待// 此該,當前線程會被添加到takeCondition的阻塞隊列里while (count == 0) {takeCondition.await();}// 出隊并返回return dequeue();} finally {lock.unlock();}}private void enqueue(E x) {items[putIndex] = x;++putIndex;if (putIndex == items.length) {putIndex = 0;}count++;// 隊列里增加元素了,可以喚醒取元素的線程了takeCondition.signal();}private E dequeue() {E x = (E) items[takeIndex];items[takeIndex] = null;++takeIndex;if (takeIndex == items.length) {takeIndex = 0;}count--;// 隊列元素減少了,騰出了位置,可喚醒放元素的線程了putCondition.signal();return x;}}

注意

  • Condition在使用之前,一定要先獲取監(jiān)視器。即調用Condition的await()和signal()方法的代碼,都必須在lock.lock()和lock.unlock之間。

  • Conditon中的await()對應Object的wait(),Condition中的signal()對應Object的notify(),Condition中的signalAll()對應Object的notifyAll()。

  • ReentrantLock與synchronized(內部鎖)性能比較

    在資源競爭不激烈的情形下,ReentrantLock性能稍微比synchronized差一點。但是當同步非常激烈的時候,synchronized的性能會下降好幾十倍。而ReentrantLock不會有太大的性能波動。

    在寫同步的時候,優(yōu)先考慮synchronized,畢竟synchronized更簡單,總得來說性能被優(yōu)化得還不錯。ReentrantLock比較復雜,寫得不好,還可能會給程序性能帶來大問題。

    Lock與synchronized區(qū)別

  • Lock鎖在拋異常時,不會自動釋放鎖,需要在finally里手動釋放。synchronized會手動釋放鎖
  • 總結

    以上是生活随笔為你收集整理的Java锁详解之ReentrantLock的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。