Java併發學習筆記6 Lock鎖

bilibili-Java併發學習筆記6 Lock鎖

基於 java 1.8.0

P24_Lock鎖機制深入詳解

/**
 * Lock 實現提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作。此實現允許更靈活的結構,可以具有差別很大的屬性,可以支持多個相關的 Condition 對象。
 * 鎖是控制多個線程對共享資源進行訪問的工具。通常,鎖提供了對共享資源的獨佔訪問。一次只能有一個線程獲得鎖,對共享資源的所有訪問都需要首先獲得鎖。不過,某些鎖可能允許對共享資源併發訪問,如 ReadWriteLock 的讀取鎖。
 * synchronized 方法或語句的使用提供了對與每個對象相關的隱式監視器鎖的訪問,但卻強制所有鎖獲取和釋放均要出現在一個塊結構中:當獲取了多個鎖時,它們必須以相反的順序釋放,且必須在與所有鎖被獲取時相同的詞法範圍內釋放所有鎖。
 * 雖然 synchronized 方法和語句的範圍機制使得使用監視器鎖編程方便了很多,而且還幫助避免了很多涉及到鎖的常見編程錯誤,但有時也需要以更爲靈活的方式使用鎖。例如,某些遍歷併發訪問的數據結果的算法要求使用 "hand-over-hand" 或 "chain locking":獲取節點 A 的鎖,然後再獲取節點 B 的鎖,然後釋放 A 並獲取 C,然後釋放 B 並獲取 D,依此類推。Lock 接口的實現允許鎖在不同的作用範圍內獲取和釋放,並允許以任何順序獲取和釋放多個鎖,從而支持使用這種技術。
 * 隨着靈活性的增加,也帶來了更多的責任。不使用塊結構鎖就失去了使用 synchronized 方法和語句時會出現的鎖自動釋放功能。在大多數情況下,應該使用以下語句:
        Lock l = ...;
        l.lock();
        try {
            // access the resource protected by this lock
        } finally {
            l.unlock();
        }
 * 鎖定和取消鎖定出現在不同作用範圍中時,必須謹慎地確保保持鎖定時所執行的所有代碼用 try-finally 或 try-catch 加以保護,以確保在必要時釋放鎖。
 * Lock 實現提供了使用 synchronized 方法和語句所沒有的其他功能,包括提供了一個非塊結構的獲取鎖嘗試 (tryLock())、一個獲取可中斷鎖的嘗試 (lockInterruptibly()) 和一個獲取超時失效鎖的嘗試 (tryLock(long, TimeUnit))。
 * Lock 類還可以提供與隱式監視器鎖完全不同的行爲和語義,如保證排序、非重入用法或死鎖檢測。如果某個實現提供了這樣特殊的語義,則該實現必須對這些語義加以記錄。
 * 注意,Lock 實例只是普通的對象,其本身可以在 synchronized 語句中作爲目標使用。獲取 Lock 實例的監視器鎖與調用該實例的任何 lock() 方法沒有特別的關係。爲了避免混淆,建議除了在其自身的實現中之外,決不要以這種方式使用 Lock 實例。
 * 除非另有說明,否則爲任何參數傳遞 null 值都將導致拋出 NullPointerException。
 * ---
 * 內存同步
 * 所有 Lock 實現都必須 實施與內置監視器鎖提供的相同內存同步語義,如 [The Java Language Specification, Third Edition (17.4 Memory Model)](https://docs.oracle.com/javase/specs/jls/se6/html/memory.html#17.4) 中所描述的:
 * - 成功的 lock 操作與成功的 Lock 操作具有同樣的內存同步效應。
 * - 成功的 unlock 操作與成功的 Unlock 操作具有同樣的內存同步效應。
 * 不成功的鎖定與取消鎖定操作以及重入鎖定/取消鎖定操作都不需要任何內存同步效果。
 * ---
 * 實現注意事項
 * 三種形式的鎖獲取(可中斷、不可中斷和定時)在其性能特徵、排序保證或其他實現質量上可能會有所不同。而且,對於給定的 Lock 類,可能沒有中斷正在進行的 鎖獲取的能力。因此,並不要求實現爲所有三種形式的鎖獲取定義相同的保證或語義,也不要求其支持中斷正在進行的鎖獲取。實現必需清楚地對每個鎖定方法所提供的語義和保證進行記錄。還必須遵守此接口中定義的中斷語義,以便爲鎖獲取中斷提供支持:完全支持中斷,或僅在進入方法時支持中斷。
 * 由於中斷通常意味着取消,而通常又很少進行中斷檢查,因此,相對於普通方法返回而言,實現可能更喜歡響應某個中斷。即使出現在另一個操作後的中斷可能會釋放線程鎖時也是如此。實現應記錄此行爲。
 * 
 * @see ReentrantLock
 * @see Condition
 * @see ReadWriteLock
 *
 * @since 1.5
 * @author Doug Lea
 */
public interface Lock {

    /**
     * Acquires the lock.
     * 獲取鎖。
     *
     * <p>If the lock is not available then the current thread becomes
     * disabled for thread scheduling purposes and lies dormant until the
     * lock has been acquired.
     * 如果鎖不可用,出於線程調度目的,將禁用當前線程,並且在獲得鎖之前,該線程將一直處於休眠狀態。
     *
     * <p><b>Implementation Considerations</b>
     * 實現注意事項
     *
     * <p>A {@code Lock} implementation may be able to detect erroneous use
     * of the lock, such as an invocation that would cause deadlock, and
     * may throw an (unchecked) exception in such circumstances.  The
     * circumstances and the exception type must be documented by that
     * {@code Lock} implementation.
     * Lock 實現可能能夠檢測到鎖的錯誤使用,比如會導致死鎖的調用,在那種環境下還可能拋出一個 (unchecked) 異常。Lock 實現必須對環境和異常類型進行記錄。
     */
    void lock();

    /**
     * Acquires the lock unless the current thread is
     * {@linkplain Thread#interrupt interrupted}. 如果當前線程未被中斷,則獲取鎖。
     *
     * <p>Acquires the lock if it is available and returns immediately. 如果鎖可用,則獲取鎖,並立即返回。
     *
     * <p>If the lock is not available then the current thread becomes
     * disabled for thread scheduling purposes and lies dormant until
     * one of two things happens: 如果鎖不可用,出於線程調度目的,將禁用當前線程,並且在發生以下兩種情況之一以前,該線程將一直處於休眠狀態:
     *
     * <ul>
     * <li>The lock is acquired by the current thread; or 鎖由當前線程獲得;或者
     * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
     * current thread, and interruption of lock acquisition is supported. 其他某個線程中斷當前線程,並且支持對鎖獲取的中斷。
     * </ul>
     *
     * <p>If the current thread: 如果當前線程
     * <ul>
     * <li>has its interrupted status set on entry to this method; or 在進入此方法時已經設置了該線程的中斷狀態;或者
     * <li>is {@linkplain Thread#interrupt interrupted} while acquiring the
     * lock, and interruption of lock acquisition is supported, 在獲取鎖時被中斷,並且支持對鎖獲取的中斷,
     * </ul>
     * then {@link InterruptedException} is thrown and the current thread's
     * interrupted status is cleared. 則將拋出 InterruptedException,並清除當前線程的已中斷狀態。
     *
     * <p><b>Implementation Considerations</b> 實現注意事項
     *
     * <p>The ability to interrupt a lock acquisition in some
     * implementations may not be possible, and if possible may be an
     * expensive operation.  The programmer should be aware that this
     * may be the case. An implementation should document when this is
     * the case. 在某些實現中可能無法中斷鎖獲取,即使可能,該操作的開銷也很大。程序員應該知道可能會發生這種情況。在這種情況下,該實現應該對此進行記錄。
     *
     * <p>An implementation can favor responding to an interrupt over
     * normal method return. 相對於普通方法返回而言,實現可能更喜歡響應某個中斷。
     *
     * <p>A {@code Lock} implementation may be able to detect
     * erroneous use of the lock, such as an invocation that would
     * cause deadlock, and may throw an (unchecked) exception in such
     * circumstances.  The circumstances and the exception type must
     * be documented by that {@code Lock} implementation. Lock 實現可能可以檢測鎖的錯誤用法,例如,某個調用可能導致死鎖,在特定的環境中可能拋出(未經檢查的)異常。該 Lock 實現必須對環境和異常類型進行記錄。
     *
     * @throws InterruptedException if the current thread is
     *         interrupted while acquiring the lock (and interruption
     *         of lock acquisition is supported) 如果在獲取鎖時,當前線程被中斷(並且支持對鎖獲取的中斷)。
     */
    void lockInterruptibly() throws InterruptedException;

    /**
     * Acquires the lock only if it is free at the time of invocation. 僅在調用時鎖爲空閒狀態才獲取該鎖。
     *
     * <p>Acquires the lock if it is available and returns immediately
     * with the value {@code true}.
     * If the lock is not available then this method will return
     * immediately with the value {@code false}. 如果鎖可用,則獲取鎖,並立即返回值 true。如果鎖不可用,則此方法將立即返回值 false。
     *
     * <p>A typical usage idiom for this method would be: 此方法的典型使用語句如下:
     *  <pre> {@code
     * Lock lock = ...;
     * if (lock.tryLock()) {
     *   try {
     *     // manipulate protected state
     *   } finally {
     *     lock.unlock();
     *   }
     * } else {
     *   // perform alternative actions
     * }}</pre>
     *
     * This usage ensures that the lock is unlocked if it was acquired, and
     * doesn't try to unlock if the lock was not acquired. 此用法可確保如果獲取了鎖,則會釋放鎖,如果未獲取鎖,則不會試圖將其釋放。
     *
     * @return {@code true} if the lock was acquired and
     *         {@code false} otherwise 如果獲取了鎖,則返回 true;否則返回 false。
     */
    boolean tryLock();

    /**
     * Acquires the lock if it is free within the given waiting time and the
     * current thread has not been {@linkplain Thread#interrupt interrupted}. 如果鎖在給定的等待時間內空閒,並且當前線程未被中斷,則獲取鎖。
     *
     * <p>If the lock is available this method returns immediately
     * with the value {@code true}.
     * If the lock is not available then
     * the current thread becomes disabled for thread scheduling
     * purposes and lies dormant until one of three things happens: 如果鎖可用,則此方法將立即返回值 true。如果鎖不可用,出於線程調度目的,將禁用當前線程,並且在發生以下三種情況之一前,該線程將一直處於休眠狀態:
     * <ul>
     * <li>The lock is acquired by the current thread; or 鎖由當前線程獲得;或者
     * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
     * current thread, and interruption of lock acquisition is supported; or 其他某個線程中斷當前線程,並且支持對鎖獲取的中斷;或者
     * <li>The specified waiting time elapses 已超過指定的等待時間
     * </ul>
     *
     * <p>If the lock is acquired then the value {@code true} is returned. 如果獲得了鎖,則返回值 true。
     *
     * <p>If the current thread: 如果當前線程:
     * <ul>
     * <li>has its interrupted status set on entry to this method; or 在進入此方法時已經設置了該線程的中斷狀態;或者
     * <li>is {@linkplain Thread#interrupt interrupted} while acquiring
     * the lock, and interruption of lock acquisition is supported, 在獲取鎖時被中斷,並且支持對鎖獲取的中斷,
     * </ul>
     * then {@link InterruptedException} is thrown and the current thread's
     * interrupted status is cleared. 則將拋出 InterruptedException,並會清除當前線程的已中斷狀態。
     *
     * <p>If the specified waiting time elapses then the value {@code false}
     * is returned.
     * If the time is
     * less than or equal to zero, the method will not wait at all. 如果超過了指定的等待時間,則將返回值 false。如果 time 小於等於 0,該方法將完全不等待。
     *
     * <p><b>Implementation Considerations</b> 實現注意事項
     *
     * <p>The ability to interrupt a lock acquisition in some implementations
     * may not be possible, and if possible may
     * be an expensive operation.
     * The programmer should be aware that this may be the case. An
     * implementation should document when this is the case. 在某些實現中可能無法中斷鎖獲取,即使可能,該操作的開銷也很大。程序員應該知道可能會發生這種情況。在這種情況下,該實現應該對此進行記錄。
     *
     * <p>An implementation can favor responding to an interrupt over normal
     * method return, or reporting a timeout. 相對於普通方法返回而言,實現可能更喜歡響應某個中斷,或者報告出現超時情況。
     *
     * <p>A {@code Lock} implementation may be able to detect
     * erroneous use of the lock, such as an invocation that would cause
     * deadlock, and may throw an (unchecked) exception in such circumstances.
     * The circumstances and the exception type must be documented by that
     * {@code Lock} implementation. Lock 實現可能可以檢測鎖的錯誤用法,例如,某個調用可能導致死鎖,在特定的環境中可能拋出(未經檢查的)異常。該 Lock 實現必須對環境和異常類型進行記錄。
     *
     * @param time the maximum time to wait for the lock 等待鎖的最長時間
     * @param unit the time unit of the {@code time} argument 參數 time 的時間單位
     * @return {@code true} if the lock was acquired and {@code false}
     *         if the waiting time elapsed before the lock was acquired 如果獲得了鎖,則返回 true;如果在獲取鎖前超過了等待時間,則返回 false
     *
     * @throws InterruptedException if the current thread is interrupted
     *         while acquiring the lock (and interruption of lock
     *         acquisition is supported) 如果在獲取鎖時,當前線程被中斷(並且支持對鎖獲取的中斷)
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    /**
     * Releases the lock. 釋放鎖。
     *
     * <p><b>Implementation Considerations</b>
     *
     * <p>A {@code Lock} implementation will usually impose
     * restrictions on which thread can release a lock (typically only the
     * holder of the lock can release it) and may throw
     * an (unchecked) exception if the restriction is violated.
     * Any restrictions and the exception
     * type must be documented by that {@code Lock} implementation.
     * Lock 實現通常對哪個線程可以釋放鎖施加了限制(通常只有鎖的保持者可以釋放它),如果違背了這個限制,可能會拋出(未經檢查的)異常。該 Lock 實現必須對所有限制和異常類型進行記錄。
     */
    void unlock();

    /**
     * Returns a new {@link Condition} instance that is bound to this
     * {@code Lock} instance. 返回綁定到此 Lock 實例的新 Condition 實例。
     *
     * <p>Before waiting on the condition the lock must be held by the
     * current thread.
     * A call to {@link Condition#await()} will atomically release the lock
     * before waiting and re-acquire the lock before the wait returns. 在等待條件前,鎖必須由當前線程保持。調用 Condition.await() 將在等待前以原子方式釋放鎖,並在等待返回前重新獲取鎖。
     *
     * <p><b>Implementation Considerations</b>
     *
     * <p>The exact operation of the {@link Condition} instance depends on
     * the {@code Lock} implementation and must be documented by that
     * implementation. Condition 實例的具體操作依賴於 Lock 實現,並且該實現必須對此加以記錄。
     *
     * @return A new {@link Condition} instance for this {@code Lock} instance 用於此 Lock 實例的新 Condition 實例
     * @throws UnsupportedOperationException if this {@code Lock}
     *         implementation does not support conditions 如果此 Lock 實現不支持條件
     */
    Condition newCondition();
}

P25_Lock鎖方法原理詳解


    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        lock.lock();
        try {
            // do something
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        if (lock.tryLock()) {
            try {
                // manipulate protected state
            } finally {
                lock.unlock();
            }
        } else {
            // perform alternative actions
        }
    }

    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        try {
            if (lock.tryLock(3L, TimeUnit.SECONDS)) {
                try {
                    // manipulate protected state
                } finally {
                    lock.unlock();
                }
            } else {
                // perform alternative actions
            }
        } catch (InterruptedException e) {
            // log
        }
    }

P26_Lock與synchronized關鍵字在底層的區別及實例分析

package new_package.thread.p24;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest2 {

    private Lock lock = new ReentrantLock();

    public void method1() {
        try {
            lock.lock();
            System.out.println("method1 invoke");
        } finally {
            // lock.unlock();
        }
    }

    public void method2() {
        try {
            lock.lock();
            System.out.println("method2 invoke");
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        LockTest2 lockTest2 = new LockTest2();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                lockTest2.method1();

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                lockTest2.method2();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

package new_package.thread.p24;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest3 {

    private Lock lock = new ReentrantLock();

    public void method1(long time) {
        try {
            lock.lock();
            System.out.println("method1 invoke");
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            lock.unlock();
        }
    }

    public void method2(long time) {
        try {
            if (lock.tryLock(800, TimeUnit.MILLISECONDS)) {
                try {
                    System.out.println("method2 invoke");
                    try {
                        Thread.sleep(time);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } finally {
                    lock.unlock();
                }
            } else {
                System.out.println("method2 not get lock");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        LockTest3 lockTest3 = new LockTest3();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                lockTest3.method1(2000);
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                lockTest3.method2(2000);
            }
        }).start();
    }
}

Lock 與 synchronized 的區別:

  • 獲取方式
    • Lock 通過代碼方式手動獲取
    • synchronized 鎖通過 JVM 獲取
  • 實現方式
    • 通過 Java 代碼方式實現
    • 通過 JVM 底層實現
  • 釋放方式
    • 務必通過 unlock() 和 finally 塊手工釋放
    • JVM 自動釋放
  • 具體類型
    • 提供了公平鎖、非公平鎖、可重入鎖
    • 可重入鎖
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章