欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的Lock與ReentrantLock深入解析

 更新時(shí)間:2024年01月22日 10:15:23   作者:我不是歐拉_  
這篇文章主要介紹了Java中的Lock與ReentrantLock深入解析,Lock位于java.util.concurrent.locks包下,是一種線程同步機(jī)制,就像synchronized塊一樣,但是,Lock比synchronized塊更靈活、更復(fù)雜,需要的朋友可以參考下

Lock

Lock位于java.util.concurrent.locks包下,是一種線程同步機(jī)制,就像synchronized塊一樣。但是,Lock比synchronized塊更靈活、更復(fù)雜。

1. Lock繼承關(guān)系

2. 官方文檔解讀

3. Lock接口方法解析

public interface Lock {
    // 獲取鎖。如果鎖不可用,則當(dāng)前線程將出于線程調(diào)度目的而禁用,并處于休眠狀態(tài),直到獲得鎖為止。
    void lock();
    // 如果當(dāng)前線程未被中斷,則獲取鎖。如果鎖可用,則獲取鎖并立即返回。
    // 如果鎖不可用,出于線程調(diào)度目的,將禁用當(dāng)前線程,該線程將一直處于休眠狀態(tài)。
    // 下面兩種情形會(huì)讓當(dāng)前線程停止休眠狀態(tài):
    // 1.鎖由當(dāng)前線程獲取。
    // 2.其他一些線程中斷當(dāng)前線程,并且支持對鎖獲取的中斷。
    // 當(dāng)前線程出現(xiàn)下面兩種情況時(shí),將拋出InterruptedException,并清除當(dāng)前線程的中斷狀態(tài)。
    // 1.當(dāng)前線程在進(jìn)入此方法時(shí),已經(jīng)設(shè)置為中斷狀態(tài)。
    // 2. 當(dāng)前線程在獲取鎖時(shí)被中斷,并且支持對鎖獲取中斷。
    void lockInterruptibly() throws InterruptedException;
    // 嘗試獲取鎖,如果鎖處于空閑狀態(tài),則獲取鎖,并立即返回true。如果鎖不可用,則立即返回false。
    // 典型用法:
    // 確保解鎖前一定獲取到鎖
    // if (lock.tryLock()) {
    //     try {
    //             // manipulate protected state
    //     } finally {
    //          lock.unlock();
    //     }
    // } else {
    //     // perform alternative actions
    // }
    boolean tryLock();
    // 該方法為tryLock()的重載方法,兩個(gè)參數(shù)分別表示為:
    // time:等待鎖的最長時(shí)間
    // unit:時(shí)間單位
    // 如果在給定的等待時(shí)間內(nèi)是空閑的并且當(dāng)前線程沒有被中斷,則獲取鎖。如果鎖可用,則此方法立即獲取鎖并返回true,如果鎖不可用,出于線程調(diào)度目的,將禁用當(dāng)前線程,該線程將一直處于休眠狀態(tài)。
    // 如果指定的等待時(shí)間超時(shí),則返回false值。如果時(shí)間小于或等于0,則該方法永遠(yuǎn)不會(huì)等待。
    // 下面三種情形會(huì)讓當(dāng)前線程停止休眠狀態(tài):
    // 1.鎖由當(dāng)前線程獲取。
    // 2.其他一些線程中斷當(dāng)前線程,并且支持對鎖獲取的中斷。
    // 3.到了指定的等待時(shí)間。
    // 當(dāng)前線程出現(xiàn)下面兩種情況時(shí),將拋出InterruptedException,并清除當(dāng)前線程的中斷狀態(tài)。
    // 1.當(dāng)前線程在進(jìn)入此方法時(shí),已經(jīng)設(shè)置為中斷狀態(tài)。
    // 2.當(dāng)前線程在獲取鎖時(shí)被中斷,并且支持對鎖獲取中斷。
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    // 釋放鎖,與lock()、tryLock()、tryLock(long , TimeUnit)、lockInterruptibly()相對應(yīng)。
    void unlock();
    // 返回綁定到此鎖實(shí)例的Condition實(shí)例。當(dāng)前線程只有獲得了鎖,才能調(diào)用Condition實(shí)例的方法。
    Condition newCondition();
}

ReentrantLock

ReentrantLock位于java.util.concurrent(J.U.C)包下,是Lock接口的實(shí)現(xiàn)類,屬于獨(dú)占鎖。可重入特性與synchronized相似,但擁有擴(kuò)展的功能。

ReentrantLock表現(xiàn)為API層面的互斥鎖,通過lock()和unlock()方法完成,是顯式的,而synchronized表現(xiàn)為原生語法層面的互斥鎖,是隱式的。

在JDK 1.6之后,虛擬機(jī)對于synchronized關(guān)鍵字進(jìn)行整體優(yōu)化后,在性能上synchronized與ReentrantLock已沒有明顯差距,因此在使用選擇上,需要根據(jù)場景而定,大部分情況下我們依然建議是synchronized關(guān)鍵字,原因之一是使用方便語義清晰,二是性能上虛擬機(jī)已為我們自動(dòng)優(yōu)化。而ReentrantLock提供了多樣化的同步特性,如超時(shí)獲取鎖、可以被中斷獲取鎖(synchronized的同步是不能中斷的)、等待喚醒機(jī)制的多個(gè)條件變量(Condition)等,因此當(dāng)我們確實(shí)需要使用到這些功能是,可以選擇ReentrantLock

ReentrantLock都是把具體實(shí)現(xiàn)委托給內(nèi)部類(Sync、NonfairSync、FairSync)

1. 可重入性

可重入性:任意線程在獲取到鎖之后能夠再次獲取該鎖而不會(huì)被鎖阻塞,synchronized和Reentrant都是可重入的,隱式顯式之分。

實(shí)現(xiàn)條件:

鎖需要去識(shí)別獲取鎖的線程是否是當(dāng)前占據(jù)鎖的線程,如果是的話,就成功獲取。鎖獲取一次,內(nèi)部鎖計(jì)數(shù)器需要加一,釋放一次減一,計(jì)數(shù)為零表示為成功釋放鎖。

ReentrantLock的重入計(jì)數(shù)是使用AbstractQueuedSynchronizer的state屬性的,state大于0表示鎖被占用,等于0表示空閑,小于0則是重入次數(shù)太多導(dǎo)致溢出了。

2. 公平鎖模式和非公平鎖模式

ReentrantLock的構(gòu)造函數(shù)接受可選的公平參數(shù),參數(shù)為true則表示獲取一個(gè)公平鎖,不帶參數(shù)或者false表示一個(gè)非公平鎖。

構(gòu)造方法:

// 無參構(gòu)造方法
// 默認(rèn)是非公平鎖模式
public ReentrantLock() {
    sync = new NonfairSync();
}
// 有參構(gòu)造方法
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

公平性鎖和非公平性鎖父類:Sync

sync繼承于AQS

static abstract class Sync extends AbstractQueuedSynchronizer {
abstract void lock();
// 非公平獲取, 非公平鎖都需要這個(gè)方法
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {   
        // state == 0表示無鎖
        // 通過CAS的方式競爭鎖,體現(xiàn)非公平性,任何線程來了不用排隊(duì)都可以搶鎖
        if (compareAndSetState(0, acquires)) {  
            // 加鎖成功,state狀態(tài)改為1
            // 當(dāng)前哪一個(gè)線程獲取到鎖,將線程信息記錄到AQS里面
            // 設(shè)置當(dāng)前持有鎖的線程
            setExclusiveOwnerThread(current);   
            return true;                      
        }
    } else if (current == getExclusiveOwnerThread()) {
            // 當(dāng)前線程正是鎖持有者,此段邏輯體現(xiàn)可重入性
            // 同一個(gè)線程可以在不獲取鎖的情況再次進(jìn)入
            // nextc表示被加鎖次數(shù),即重入次數(shù)
            int nextc = c + acquires;
            if (nextc < 0) // 被鎖次數(shù)上溢(很少出現(xiàn))
                throw new Error("Maximum lock count exceeded");
            // 設(shè)置加鎖次數(shù),lock幾次就要unlock幾次,否則無法釋放鎖
            setState(nextc);
            return true;
        }
        return false;
    }
    // 釋放
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        // 只有鎖的持有者才能釋放鎖
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {   // 鎖被釋放
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
    // 當(dāng)前線程是否持有鎖
    protected final boolean isHeldExclusively() {
        return getExclusiveOwnerThread() == Thread.currentThread();
    }
    final ConditionObject newCondition() {
        return new ConditionObject();
    }
    // 鎖的持有者
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }
    // 加鎖次數(shù)
    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }
    // 是否上鎖,根據(jù)state字段可以判斷
    final boolean isLocked() {
        return getState() != 0;
    }
}

公平鎖模式:FairSync

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        final void lock() {
            acquire(1);
        }
        // 方法邏輯與Snyc中的nonfairTryAcquire方法一致,只是加了線程排隊(duì)的邏輯
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // !hasQueuedPredecessors()
                // 判斷隊(duì)列中是否有其他線程,沒有才進(jìn)行鎖的獲取,否則繼續(xù)排隊(duì)
                // 體現(xiàn)公平性
                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;
        }
    }

非公平鎖模式:NonfairSync

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            // 不管鎖是否已經(jīng)被占用,采用CAS的方式進(jìn)行鎖競爭
            // 搶鎖成功,則把當(dāng)前線程設(shè)置為活躍的線程
            // 搶鎖失敗則走acquire(1)邏輯
            // 體現(xiàn)非公平性,線程不必排隊(duì)
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 搶鎖失敗,調(diào)用tryAcquire方法,最終調(diào)用nonfairTryAcquire方法
                acquire(1);
        }
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

3. condition

在并發(fā)編程中,每個(gè)Java對象都存在一組監(jiān)視器方法,如wait()、notify()以及notifyAll()方法,通過這些方法,我們可以實(shí)現(xiàn)線程間通信與協(xié)作(也稱為等待喚醒機(jī)制),如生產(chǎn)者-消費(fèi)者模式、等待-通知模式,而且這些方法必須配合著synchronized關(guān)鍵字使用。

與synchronized的等待喚醒機(jī)制相比Condition具有更多的靈活性以及精確性,這是因?yàn)閚otify()在喚醒線程時(shí)是隨機(jī)(同一個(gè)鎖),而Condition則可通過多個(gè)Condition實(shí)例對象建立更加精細(xì)的線程控制,也就帶來了更多靈活性了,我們可以簡單理解為以下兩點(diǎn):

  1. 通過Condition能夠精細(xì)的控制多線程的休眠與喚醒。
  2. 對于一個(gè)鎖,我們可以為多個(gè)線程間建立不同的Condition。

Condition相關(guān)方法

    // 返回綁定到此鎖實(shí)例的Condition實(shí)例。當(dāng)前線程只有獲得了鎖,才能調(diào)用Condition實(shí)例的方法。
    Condition newCondition();
public interface Condition {
    // 線程程進(jìn)入等待狀態(tài)直到被通知(signal)或中斷
    // 當(dāng)其他線程調(diào)用singal()或singalAll()方法時(shí),該線程將被喚醒
    // 當(dāng)其他線程調(diào)用interrupt()方法中斷當(dāng)前線程
    // await()相當(dāng)于synchronized等待喚醒機(jī)制中的wait()方法
    void await() throws InterruptedException;
    // 與wait()方法相同,唯一的不同點(diǎn)是,該方法不會(huì)再等待的過程中響應(yīng)中斷
    void awaitUninterruptibly();
    // 當(dāng)前線程進(jìn)入等待狀態(tài),直到被喚醒或被中斷或超時(shí)
    // 其中nanosTimeout指的等待超時(shí)時(shí)間,單位納秒
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    // 同awaitNanos,但可以指明時(shí)間單位
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    // 線程進(jìn)入等待狀態(tài),直到被喚醒、中斷或到達(dá)某個(gè)時(shí)
    // 間期限(deadline),如果沒到指定時(shí)間就被喚醒,返回true,其他情況返回false
    boolean awaitUntil(Date deadline) throws InterruptedException;
    // 喚醒一個(gè)等待在Condition上的線程,該線程從等待方法返回前必須
    // 獲取與Condition相關(guān)聯(lián)的鎖,功能與notify()相同
    void signal();
    // 喚醒所有等待在Condition上的線程,該線程從等待方法返回前必須
    // 獲取與Condition相關(guān)聯(lián)的鎖,功能與notifyAll()相同
    void signalAll();
}

使用await之前必須加鎖,使用signal、signalAll之后記得釋放鎖。

Condition實(shí)現(xiàn)原理

Condition的具體實(shí)現(xiàn)類是AQS的內(nèi)部類ConditionObject,前面我們分析過AQS中存在兩種隊(duì)列,一種是同步隊(duì)列,一種是等待隊(duì)列,而等待隊(duì)列就相對于Condition而言的。注意在使用Condition前必須獲得鎖,同時(shí)在Condition的等待隊(duì)列上的結(jié)點(diǎn)與前面同步隊(duì)列的結(jié)點(diǎn)是同一個(gè)類即Node,其結(jié)點(diǎn)的waitStatus的值為CONDITION。在實(shí)現(xiàn)類ConditionObject中有兩個(gè)結(jié)點(diǎn)分別是firstWaiter和lastWaiter,firstWaiter代表等待隊(duì)列第一個(gè)等待結(jié)點(diǎn),lastWaiter代表等待隊(duì)列最后一個(gè)等待結(jié)點(diǎn),代碼如下:

public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;
        /** First node of condition queue. */
        // 等待隊(duì)列第一個(gè)等待結(jié)點(diǎn)
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        // 等待隊(duì)列最后一個(gè)等待結(jié)點(diǎn)
        private transient Node lastWaiter;
        /**
         * Creates a new {@code ConditionObject} instance.
         */
        public ConditionObject() { }
}

每個(gè)Condition都對應(yīng)著一個(gè)等待隊(duì)列,也就是說如果一個(gè)鎖上創(chuàng)建了多個(gè)Condition對象,那么也就存在多個(gè)等待隊(duì)列。等待隊(duì)列是一個(gè)FIFO的隊(duì)列,在隊(duì)列中每一個(gè)節(jié)點(diǎn)都包含了一個(gè)線程的引用,而該線程就是Condition對象上等待的線程。當(dāng)一個(gè)線程調(diào)用了await()相關(guān)的方法,那么該線程將會(huì)釋放鎖,并構(gòu)建一個(gè)Node節(jié)點(diǎn)封裝當(dāng)前線程的相關(guān)信息加入到等待隊(duì)列中進(jìn)行等待,直到被喚醒、中斷、超時(shí)才從隊(duì)列中移出。Condition中的等待隊(duì)列模型如下:

Node節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu),在等待隊(duì)列中使用的變量與同步隊(duì)列是不同的,Condtion中等待隊(duì)列的結(jié)點(diǎn)只有直接指向的后繼結(jié)點(diǎn)并沒有指明前驅(qū)結(jié)點(diǎn),而且使用的變量是nextWaiter而不是next。

firstWaiter指向等待隊(duì)列的頭結(jié)點(diǎn),lastWaiter指向等待隊(duì)列的尾結(jié)點(diǎn),等待隊(duì)列中結(jié)點(diǎn)的狀態(tài)只有兩種即CANCELLED和CONDITION,前者表示線程已結(jié)束需要從等待隊(duì)列中移除,后者表示條件結(jié)點(diǎn)等待被喚醒。再次強(qiáng)調(diào)每個(gè)Codition對象對于一個(gè)等待隊(duì)列,也就是說AQS中只能存在一個(gè)同步隊(duì)列,但可擁有多個(gè)等待隊(duì)列。

實(shí)現(xiàn)代碼:

await方法:

public final void await() throws InterruptedException {
      // 判斷線程是否被中斷
      if (Thread.interrupted())
          throw new InterruptedException();
      // 創(chuàng)建新結(jié)點(diǎn)加入等待隊(duì)列并返回
      Node node = addConditionWaiter();
      // 釋放當(dāng)前線程鎖即釋放同步狀態(tài)
      int savedState = fullyRelease(node);
      int interruptMode = 0;
      // 判斷結(jié)點(diǎn)是否同步隊(duì)列(SyncQueue)中,即是否被喚醒
      while (!isOnSyncQueue(node)) {
          // 掛起線程
          LockSupport.park(this);
          // 判斷是否被中斷喚醒,如果是退出循環(huán)。
          if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
              break;
      }
      // 被喚醒后執(zhí)行自旋操作爭取獲得鎖,同時(shí)判斷線程是否被中斷
      if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
          interruptMode = REINTERRUPT;
       // clean up if cancelled
      if (node.nextWaiter != null) 
          // 清理等待隊(duì)列中不為CONDITION狀態(tài)的結(jié)點(diǎn)
          unlinkCancelledWaiters();
      if (interruptMode != 0)
          reportInterruptAfterWait(interruptMode);
  }
private Node addConditionWaiter() {
    Node t = lastWaiter;
      // 判斷是否為結(jié)束狀態(tài)的結(jié)點(diǎn)并移除
      if (t != null && t.waitStatus != Node.CONDITION) {
          unlinkCancelledWaiters();
          t = lastWaiter;
      }
      // 創(chuàng)建新結(jié)點(diǎn)狀態(tài)為CONDITION
      Node node = new Node(Thread.currentThread(), Node.CONDITION);
      // 加入等待隊(duì)列
      if (t == null)
          firstWaiter = node;
      else
          t.nextWaiter = node;
      lastWaiter = node;
      return node;
}

signal()方法:

 public final void signal() {
   // 判斷是否持有獨(dú)占鎖,如果不是拋出異常
   // 從這點(diǎn)也可以看出只有獨(dú)占模式先采用等待隊(duì)列,而共享模式下是沒有等待隊(duì)列的,也就沒法使用Condition
   if (!isHeldExclusively())
          throw new IllegalMonitorStateException();
      Node first = firstWaiter;
      // 喚醒等待隊(duì)列第一個(gè)結(jié)點(diǎn)的線程
      if (first != null)
          doSignal(first);
 }
 private void doSignal(Node first) {
     do {
             // 移除條件等待隊(duì)列中的第一個(gè)結(jié)點(diǎn),
             // 如果后繼結(jié)點(diǎn)為null,那么說沒有其他結(jié)點(diǎn)將尾結(jié)點(diǎn)也設(shè)置為null
            if ( (firstWaiter = first.nextWaiter) == null)
                 lastWaiter = null;
             first.nextWaiter = null;
          // 如果被通知節(jié)點(diǎn)沒有進(jìn)入到同步隊(duì)列并且條件等待隊(duì)列還有不為空的節(jié)點(diǎn),則繼續(xù)循環(huán)通知后續(xù)結(jié)點(diǎn)
         } while (!transferForSignal(first) &&
                  (first = firstWaiter) != null);
        }
// transferForSignal方法
final boolean transferForSignal(Node node) {
    // 嘗試設(shè)置喚醒結(jié)點(diǎn)的waitStatus為0,即初始化狀態(tài)
    // 如果設(shè)置失敗,說明當(dāng)期結(jié)點(diǎn)node的waitStatus已不為
    // CONDITION狀態(tài),那么只能是結(jié)束狀態(tài)了,因此返回false
    // 返回doSignal()方法中繼續(xù)喚醒其他結(jié)點(diǎn)的線程,注意這里并
    // 不涉及并發(fā)問題,所以CAS操作失敗只可能是預(yù)期值不為CONDITION,
    // 而不是多線程設(shè)置導(dǎo)致預(yù)期值變化,畢竟操作該方法的線程是持有鎖的。
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
         return false;
        // 加入同步隊(duì)列并返回前驅(qū)結(jié)點(diǎn)p
        Node p = enq(node);
        int ws = p.waitStatus;
        // 判斷前驅(qū)結(jié)點(diǎn)是否為結(jié)束結(jié)點(diǎn)(CANCELLED=1)或者在設(shè)置
        // 前驅(qū)節(jié)點(diǎn)狀態(tài)為Node.SIGNAL狀態(tài)失敗時(shí),喚醒被通知節(jié)點(diǎn)代表的線程
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            // 喚醒node結(jié)點(diǎn)的線程
            LockSupport.unpark(node.thread);
        return true;
    }

流程:signal()被調(diào)用后,先判斷當(dāng)前線程是否持有獨(dú)占鎖,如果有,那么喚醒當(dāng)前Condition對象中等待隊(duì)列的第一個(gè)結(jié)點(diǎn)的線程,并從等待隊(duì)列中移除該結(jié)點(diǎn),移動(dòng)到同步隊(duì)列中,如果加入同步隊(duì)列失敗,那么繼續(xù)循環(huán)喚醒等待隊(duì)列中的其他結(jié)點(diǎn)的線程,如果成功加入同步隊(duì)列,那么如果其前驅(qū)結(jié)點(diǎn)是否已結(jié)束或者設(shè)置前驅(qū)節(jié)點(diǎn)狀態(tài)為Node.SIGNAL狀態(tài)失敗,則通過LockSupport.unpark()喚醒被通知節(jié)點(diǎn)代表的線程,到此signal()任務(wù)完成。

注意被喚醒后的線程,將從前面的await()方法中的while循環(huán)中退出,因?yàn)榇藭r(shí)該線程的結(jié)點(diǎn)已在同步隊(duì)列中,那么while (!isOnSyncQueue(node))將不在符合循環(huán)條件,進(jìn)而調(diào)用AQS的acquireQueued()方法加入獲取同步狀態(tài)的競爭中,這就是等待喚醒機(jī)制的整個(gè)流程實(shí)現(xiàn)原理,流程如下圖所示(注意無論是同步隊(duì)列還是等待隊(duì)列使用的Node數(shù)據(jù)結(jié)構(gòu)都是同一個(gè),不過是使用的內(nèi)部變量不同罷了) 

流程圖:

Condition它更強(qiáng)大的地方在于:能夠更加精細(xì)的控制多線程的休眠與喚醒。對于同一個(gè)鎖,我們可以創(chuàng)建多個(gè)Condition,在不同的情況下使用不同的Condition。例如,假如多線程讀/寫同一個(gè)緩沖區(qū):當(dāng)向緩沖區(qū)中寫入數(shù)據(jù)之后,喚醒"讀線程";當(dāng)從緩沖區(qū)讀出數(shù)據(jù)之后,喚醒"寫線程";并且當(dāng)緩沖區(qū)滿的時(shí)候,"寫線程"需要等待;當(dāng)緩沖區(qū)為空時(shí),"讀線程"需要等待。如果采用Object類中的wait(),notify(),notifyAll()實(shí)現(xiàn)該緩沖區(qū),當(dāng)向緩沖區(qū)寫入數(shù)據(jù)之后需要喚醒"讀線程"時(shí),不可能通過notify()或notifyAll()明確的指定喚醒"讀線程",而只能通過notifyAll喚醒所有線程(但是notifyAll無法區(qū)分喚醒的線程是讀線程,還是寫線程)。   但是,通過Condition,就能明確的指定喚醒讀線程。

利用Condition實(shí)現(xiàn)等待-通知模式:

public class ABCThread implements Runnable {
    private ReentrantLock lock;
    private Condition sCondition;
    private Condition aCondition;
    public ABCThread(ReentrantLock lock, Condition sCondition, Condition aCondition) {
        this.lock = lock;
        this.sCondition = sCondition; // 喚醒下一個(gè)線程
        this.aCondition = aCondition; // 阻塞當(dāng)前線程
    }
    @Override
    public void run() {
        int i = 0;
        while (i < 10) {
            // 加鎖
            lock.lock();
            try {
                // 接收到線程通知?jiǎng)t繼續(xù)執(zhí)行,否則就阻塞
                aCondition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.print(Thread.currentThread().getName()+" ");
            // 喚醒其他線程線程
            sCondition.signal();
            i++;
            // 釋放鎖
            lock.unlock();
        }
    }
}
        ReentrantLock reentrantLock = new ReentrantLock();
        Condition ab = reentrantLock.newCondition();
        Condition bc = reentrantLock.newCondition();
        Condition ca = reentrantLock.newCondition();
        new Thread(new ABCThread(reentrantLock,ab,ca),"A").start();
        new Thread(new ABCThread(reentrantLock,bc,ab),"B").start();
        new Thread(new ABCThread(reentrantLock,ca,bc),"C").start();
        reentrantLock.lock();
        ca.signal();
        reentrantLock.unlock();

到此這篇關(guān)于Java中的Lock與ReentrantLock深入解析的文章就介紹到這了,更多相關(guān)Java的Lock與ReentrantLock內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java實(shí)現(xiàn)的猴子吃桃問題算法示例

    Java實(shí)現(xiàn)的猴子吃桃問題算法示例

    這篇文章主要介紹了Java實(shí)現(xiàn)的猴子吃桃問題算法,簡單描述了猴子吃桃問題并結(jié)合實(shí)例形式給出了java解決猴子吃桃問題的具體實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-10-10
  • java base64編碼、解碼的三種方式總結(jié)

    java base64編碼、解碼的三種方式總結(jié)

    這篇文章主要介紹了java base64編碼、解碼的三種方式,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-10-10
  • Java列表元素自定義排序方式

    Java列表元素自定義排序方式

    文章介紹了在Java開發(fā)中如何對列表元素進(jìn)行自定義排序,通過實(shí)現(xiàn)`Comparator`接口并重寫`compare`方法來指定自定義排序規(guī)則,示例展示了如何對漢字?jǐn)?shù)字進(jìn)行排序,并通過改變自定義順序列表的元素添加順序來實(shí)現(xiàn)倒序排序
    2024-12-12
  • Spring Cloud Gateway去掉url前綴

    Spring Cloud Gateway去掉url前綴

    這篇文章主要介紹了Spring Cloud Gateway去掉url前綴的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • IDEA maven上傳速度很慢的解決辦法

    IDEA maven上傳速度很慢的解決辦法

    maven上傳的速度很慢,排除網(wǎng)絡(luò)原因,需要檢查配置,本文主要介紹了IDEA maven上傳速度很慢的解決辦法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-08-08
  • java實(shí)現(xiàn)租車系統(tǒng)

    java實(shí)現(xiàn)租車系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)租車系統(tǒng),以及遇到的兩個(gè)問題解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • MyBatis-Plus自動(dòng)填充字段的詳細(xì)教程

    MyBatis-Plus自動(dòng)填充字段的詳細(xì)教程

    今天編寫一個(gè)詳細(xì)的教程來介紹如何在?Spring?Boot?項(xiàng)目中使用?MyBatis-Plus?實(shí)現(xiàn)自動(dòng)填充時(shí)間字段(如創(chuàng)建時(shí)間?createTime?和更新時(shí)間?updateTime),可以分為以下幾個(gè)部分,這個(gè)教程將涵蓋從項(xiàng)目配置到自動(dòng)填充的完整過程,需要的朋友可以參考下
    2024-08-08
  • Java中equals和==的區(qū)別詳解

    Java中equals和==的區(qū)別詳解

    這篇文章主要介紹了詳解 Java 中 equals 和 == 的區(qū)別的相關(guān)資料,equals 和 == 都是用來檢測兩個(gè)字符串是否相等,返回值也都是布爾型,但是兩者在內(nèi)部比較的處理中卻不盡相同需要的朋友可以參考下
    2021-09-09
  • 詳解springboot WebTestClient的使用

    詳解springboot WebTestClient的使用

    WebClient是一個(gè)響應(yīng)式客戶端,它提供了RestTemplate的替代方法。這篇文章主要介紹了詳解springboot WebTestClient的使用, 具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • java中刪除文件/文件夾的3種方法示例小結(jié)

    java中刪除文件/文件夾的3種方法示例小結(jié)

    這篇文章主要介紹了java中刪除文件/文件夾的3種方法示例小結(jié),第一種是通過io刪除文件,第二種是通過Files.walk刪除文件,第三種是通過 Files.walkFileTree刪除文件,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2023-10-10

最新評(píng)論