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 自動釋放
- 具體類型
- 提供了公平鎖、非公平鎖、可重入鎖
- 可重入鎖