Lock鎖介面實現
阿新 • • 發佈:2020-12-02
Lock鎖介面實現
學習材料來源於網路
如有侵權,聯絡刪除
原始碼
package java.util.concurrent.locks; import java.util.concurrent.TimeUnit; /** * 與使用{@code sync}方法和語句相比,{@ code Lock}實現提供了更廣泛的鎖定 * 操作。它們允許更靈活的結構,可能具有完全不同的屬性,並且可能支援多個關聯的{@link Condition}物件。 * * 鎖是用於控制多個執行緒對共享資源的訪問的工具。通常,鎖提供對共享資源的獨佔訪問: * 一次只能有一個執行緒可以獲取該鎖,並且對共享資源的所有訪問都需要首先獲取該鎖。 * 但是,某些鎖可能允許併發訪問共享資源,例如{@link ReadWriteLock}的讀取鎖。 * * <p>使用{@code Synchronized}方法或語句可 * 訪問與每個物件關聯的隱式監視器鎖,但是 * 強制所有鎖的獲取和釋放以塊結構方式進行: * 當多個鎖被使用時被獲取的鎖必須以相反的 * 順序釋放,並且所有鎖都必須在與被獲取的鎖相同的詞法範圍內釋放。 * * <p>雖然{@code sync}方法的作用域機制 * 和語句使使用監視器鎖的程式設計變得容易得多,並且 * 並避免了很多常見的涉及鎖的程式設計錯誤,但是 * 在某些情況下您需要使用鎖以更靈活的方式。例如,某些用於遍歷併發訪問的資料結構的演算法需要使用“交接”或“鏈鎖”:您 * 取得節點A的鎖,然後取得節點B,然後釋放A並取得 * C ,然後釋放B並獲取D,依此類推。 * {@code Lock}介面的實現通過 * 允許在不同範圍內獲取和釋放鎖,並允許以任意 * 順序獲取和釋放多個鎖,從而啟用了此類技術。 * * <p>隨著靈活性的提高,額外的責任也隨之增加。缺少塊結構鎖定將消除 * {{code sync}方法和語句中發生的鎖定的自動釋放。在大多數情況下,應使用以下習慣用語 * : * * <pre> {@code * Lock l = ...; * l.lock(); * try { * // access the resource protected by this lock * } finally { * l.unlock(); * }}</pre> * *當鎖定和解鎖發生在不同的範圍內時, *必須小心以確保通過try-finally或try-catch保護在保持鎖的同時執行的所有程式碼, *以確保在必要時釋放鎖。 * * <p> {@ code Lock}類還可以提供與隱式監視器鎖完全不同的行為和語義, * 例如,保證順序,不可重用或死鎖。如果實現提供了這種特殊的語義 *,那麼實現必須記錄這些語義。 * * <p> {@ code Lock}類還可以提供與隱式監視器鎖完全不同的行為和語義, * 例如,保證順序,不可重用或死鎖。如果實現提供了這種特殊的語義, * 那麼實現必須記錄這些語義。 * * <p>請注意,{@ code Lock}例項只是普通物件,它們自身可以用作{@code Synchronized}語句中的目標。 * 獲取{@code Lock}例項的監視器鎖定與該例項的任何{@link #lock}方法都沒有指定的關係 *。 * 為避免混淆,建議您不要以這種方式使用{@code Lock} * 例項,除非在它們自己的實現中使用。 * * <p>除非另有說明,否則為任何 * 引數傳遞{@code null}值將導致丟擲{@link NullPointerException}。 * * <h3>記憶體同步</h3> * * <p>所有{@code Lock}實現<em>必須</ em>強制執行與內建監視器鎖相同的 * 記憶體同步語義,如 * <a href =“ https:// docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4“> * Java語言規範(17.4記憶體模型)</a>: * <ul> * <li>成功的{@code lock}操作具有與成功的<em> Lock </ em>操作相同的記憶體*同步效果。 * <li>成功的{@code unlock}操作與成功的<em> Unlock </ em>操作具有相同的記憶體同步效果。 * </ ul> * * 不成功的鎖定和解鎖操作,以及可重入的 * 鎖定/解鎖操作,不需要任何記憶體*同步效果。 * * <h3>實施注意事項s</h3> * * <p>鎖獲取的三種形式(可中斷, * 不可中斷和定時)在效能,特性,訂購保證或其他實現質量上可能有所不同。 * 此外,在給定的{@code Lock} * 類中,中斷<em> inginging </ em> *獲取鎖的功能可能不可用。 * 因此,不需要實現為所有三種形式的鎖獲取定義完全相同的保證或語義, * 也不需要支援中斷正在進行的鎖獲取。需要一個實現來清楚地*記錄每個鎖定方法所提供的語義和保證。 * 在支援鎖獲取中斷的程度上,它還必須遵守此介面中定義的中斷語義:或者完全或僅在方法輸入時才這樣做。 * * <p>由於中斷通常意味著取消,並且對中斷的檢查通常是很少的,因此與正常方法返回相比,實現可能更喜歡對中斷做出響應。即使可以 * 表明在另一個操作之後發生的中斷可能已經取消阻塞了執行緒,也是如此。實現應記錄此行為。 * * @see ReentrantLock * @see Condition * @see ReadWriteLock * * @since 1.5 * @author Doug Lea */ public interface Lock { / ** *獲取鎖。 * * <p>如果該鎖不可用,則出於執行緒排程目的,當前執行緒將被禁用 *,並且在獲取該鎖之前,該執行緒處於休眠狀態。 * * <p> <b>實現注意事項</ b> * * <p> {@ code Lock}實現可能能夠檢測對鎖的錯誤使用,例如可能導致死鎖的呼叫,並且 * 在這種情況下丟擲一個(未經檢查的)異常。 *環境和異常型別必須由該{@code Lock}實現來記錄。 * / void lock(); /** *獲取鎖定,除非當前執行緒被{{@linkplain Thread#interrupt interrupted}破壞。 * * <p>獲取鎖(如果有)並立即返回。 * * <p>如果該鎖不可用,則出於執行緒排程目的,當前執行緒將被禁用*並處於休眠狀態,直到發生以下兩種情況之一: * * <ul> * <li>該鎖由當前執行緒獲取;或 * <li>當前執行緒有一些其他執行緒{@linkplain Thread#interrupt interrupts},並且支援中斷獲取鎖。 * </ ul> * * <p>如果當前執行緒: * <ul> * <li>在進入此方法時已設定其中斷狀態;或 * <li>在獲取 * 鎖的過程中被{@linkplain Thread#interrupt interrupted中斷},並且支援中斷獲取鎖, * </ ul> *然後丟擲{@link InterruptedException}並且當前執行緒的 * 中斷狀態為已清除。 * * <p> <b>實施注意事項</ b> * * <p>在某些*實現中可能無法中斷鎖獲取,並且如果可能的話,可能是 * 昂貴的操作。程式設計師應意識到可能是這種情況。在這種情況下,實現應記錄在案。 * * <p>與正常方法返回相比,實現可能更喜歡對中斷做出響應。 * * <p> {@ code Lock}實現可能能夠檢測到*錯誤的使用鎖,例如可能導致死鎖的呼叫, * 並且在這種情況下可能引發(未經檢查的)異常。環境和異常型別必須*由該{@code Lock}實現來記錄。 * * @throws InterruptedException如果當前執行緒在獲取鎖時被中斷(並且支援鎖獲取的中斷) */ void lockInterruptibly() throws InterruptedException; /** * 僅在呼叫時釋放鎖時才獲取鎖。 * * <p>獲取該鎖(如果有)並立即返回 *,其值為{@code true}。 * 如果鎖不可用,則此方法將立即返回 *,其值為{@code false}。 * * <p>此方法的典型用法是: * <pre> {@code * Lock lock = ...; * if (lock.tryLock()) { * try { * // 操縱保護狀態 * } finally { * lock.unlock(); * } * } else { * // 執行替代動作 * }}</pre> * * 此用法可確保在獲取鎖後將其解鎖,並且 *在未獲取鎖時不會嘗試解鎖。 * * @return {@code true} 如果獲得了鎖,並且 * {@code false} 否則 */ boolean tryLock(); /** *如果鎖在給定的等待時間內是空閒的,並且 *當前執行緒尚未{@linkplain Thread#interrupt interrupted},則獲取該鎖。 * * <p>如果鎖可用,則此方法立即返回 *,其值為{@code true}。 * 如果該鎖不可用,則*當前執行緒將出於執行緒排程目的而被禁用 * 並處於休眠狀態,直到發生以下三種情況之一: * <ul> * <li>該鎖是由當前執行緒獲取的;要麼 * <li>當前執行緒的其他一些執行緒 * {@linkplain Thread#interrupt interrupts},並支援鎖定獲取的中斷;要麼 * <li>經過指定的等待時間 * </ul> * * <p>如果獲得了鎖,則返回值{@code true}。 * * <p>如果當前執行緒: * <ul> * <li>在進入此方法時已設定其中斷狀態;要麼 * <li>在獲取*鎖的過程中被{@linkplain Thread#interrupt interrupted中斷了,並且支援中斷獲取鎖, * </ul> * 然後丟擲{@link InterruptedException}並清除當前執行緒的 * 中斷狀態。 * * <p>如果經過了指定的等待時間,則返回值{@code false} *。 *如果時間*小於或等於零,則該方法將完全不等待。 * * <p><b>實施注意事項</b> * * <p>在某些實現中,中斷鎖獲取的能力*可能是不可能的,並且如果可能的話 * 是一項昂貴的操作。 *程式設計師應意識到可能是這種情況。在這種情況下, * 實現應記錄在案。 * * <p>與正常方法返回或報告超時相比,實現可能更喜歡響應中斷。 * * <p> {@ code Lock}實現可以檢測*錯誤使用鎖,例如可能導致死鎖的呼叫,並且在這種情況下可能引發(未經檢查的)異常。 * 必須通過該{@code Lock}實現來記錄情況和異常型別。 * * @param time等待鎖的最長時間 * @param {@code time}引數的時間單位 * @return {@code true},如果已獲得鎖,則{@code false} * 獲取鎖之前是否經過了等待時間 * * @throws InterruptedException-如果當前執行緒在獲取鎖時被中斷 *(並且支援鎖的中斷 * 支援獲取) */ boolean tryLock(long time, TimeUnit unit) throws InterruptedException; /** * 釋放鎖。 * * <p><b>實施注意事項</b> * * <p>{@code Lock}實現通常會對可以釋放鎖的執行緒施加 *限制(通常只有 * *鎖的持有者可以釋放該鎖),並且如果違反該限制,則可能引發 *(未經檢查)異常。 *任何限制和例外 *型別必須由該{@code Lock}實現來記錄。 */ void unlock(); /** *返回繫結到該 * {@code Lock}例項的新{@link Condition}例項。 * * <p>在等待條件之前,鎖必須由當前執行緒持有。 *對{@link Condition#await()}的呼叫將在等待之前自動釋放鎖 *,並在等待返回之前重新獲取該鎖。 * * <p><b>實施注意事項</b> * * <p> {@ link Condition}例項的確切操作取決於{@code Lock}實現,並且必須由該實現記錄下來。 * * @return 此{@code Lock}例項的新{@link Condition}例項 * @throws UnsupportedOperationException如果此{@code Lock} * 實施不支援條件 */ Condition newCondition(); }
核心API
方法 | 描述 |
---|---|
lock | 獲取鎖的方法,若被其他執行緒佔用,則會等待(阻塞) |
lockInterruptibly | 在鎖的獲取過程中可以中斷當前執行緒 |
tryLock | 嘗試非阻塞地獲取鎖,立即返回 |
unlocj | 釋放鎖 |
提示:根據Lock介面的原始碼註釋,Lock介面的實現,具備和同步關鍵字同樣的記憶體語義。
ReentrantLock
獨享鎖;支援公平鎖、非公平鎖兩種模式;可重入
示例1
// 3、 演示可重入 public class ReentrantDemo1 { private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { //第一次加鎖 lock.lock(); try { System.out.println("第一次獲取鎖"); System.out.println("當前執行緒獲取鎖的次數" + lock.getHoldCount()); //第二次加鎖 lock.lock(); System.out.println("第二次獲取鎖了"); System.out.println("當前執行緒獲取鎖的次數" + lock.getHoldCount()); }finally { //釋放第二次鎖 lock.unlock(); //釋放第一次鎖 lock.unlock(); } System.out.println("當前執行緒獲取鎖的次數" + lock.getHoldCount()); // 如果不釋放,此時其他執行緒是拿不到鎖的 new Thread(() -> { System.out.println(Thread.currentThread() + " 期望搶到鎖"); lock.lock(); System.out.println(Thread.currentThread() + " 執行緒拿到了鎖"); }).start(); } }
正確的停止等待鎖的執行緒
示例2
package icu.shaoyayu.multithreading.chapter4; import java.util.ArrayList; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author shaoyayu * @E_Mail * @Version 1.0.0 * @readme : */ // ReentrantLock 可重入鎖示例 public class ReentrantLockDemo1 { private Lock lock = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { ReentrantLockDemo1 demo1 = new ReentrantLockDemo1(); Runnable runnable = new Runnable() { @Override public void run() { try { demo1.test(Thread.currentThread()); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread thread1 = new Thread(runnable); Thread thread2 = new Thread(runnable); thread1.start(); Thread.sleep(500); // 等待0.5秒,讓thread1先執行 thread2.start(); Thread.sleep(2000); // 兩秒後,中斷thread2 thread2.interrupt(); } public void test(Thread thread) throws InterruptedException { System.out.println(Thread.currentThread().getName() + ", 想獲取鎖"); lock.lock(); //注意,如果需要正確中斷等待鎖的執行緒,必須將獲取鎖放在外面,然後將InterruptedException丟擲 try { System.out.println(thread.getName() + "得到了鎖"); Thread.sleep(10000); // 搶到鎖,10秒不釋放 } finally { System.out.println(Thread.currentThread().getName() + "執行finally"); lock.unlock(); System.out.println(thread.getName() + "釋放了鎖"); } } }
結果:
Thread-0, 想獲取鎖
Thread-0得到了鎖
Thread-1, 想獲取鎖
Thread-0執行finally
Thread-0釋放了鎖
Thread-1得到了鎖
Thread-1執行finally
Thread-1釋放了鎖
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at icu.shaoyayu.multithreading.chapter4.ReentrantLockDemo1.test(ReentrantLockDemo1.java:46)
at icu.shaoyayu.multithreading.chapter4.ReentrantLockDemo1$1.run(ReentrantLockDemo1.java:24)
at java.lang.Thread.run(Thread.java:748)
對程式碼進行修改
// 可響應中斷
public class LockInterruptiblyDemo1 {
private Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
LockInterruptiblyDemo1 demo1 = new LockInterruptiblyDemo1();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
demo1.test(Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
thread1.start();
Thread.sleep(500); // 等待0.5秒,讓thread1先執行
thread2.start();
Thread.sleep(2000); // 兩秒後,中斷thread2
thread2.interrupt();
}
public void test(Thread thread) throws InterruptedException {
System.out.println(Thread.currentThread().getName() + ", 想獲取鎖");
lock.lockInterruptibly(); //注意,如果需要正確中斷等待鎖的執行緒,必須將獲取鎖放在外面,然後將InterruptedException丟擲
try {
System.out.println(thread.getName() + "得到了鎖");
Thread.sleep(10000); // 搶到鎖,10秒不釋放
} finally {
System.out.println(Thread.currentThread().getName() + "執行finally");
lock.unlock();
System.out.println(thread.getName() + "釋放了鎖");
}
}
}
執行結果:
Thread-0, 想獲取鎖
Thread-0得到了鎖
Thread-1, 想獲取鎖
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at icu.shaoyayu.multithreading.chapter4.LockInterruptiblyDemo1.test(LockInterruptiblyDemo1.java:42)
at icu.shaoyayu.multithreading.chapter4.LockInterruptiblyDemo1$1.run(LockInterruptiblyDemo1.java:23)
at java.lang.Thread.run(Thread.java:748)
Thread-0執行finally
Thread-0釋放了鎖
ReadWriteLock
維護一對關聯鎖,一個用於只讀操作,一個用於寫入;讀鎖可以由多個讀執行緒同時持有,寫鎖是排他的。
適合讀取執行緒比寫入執行緒多的場景,改進互斥鎖的效能,示例場景:快取元件、集合的併發執行緒安全性改造。
鎖降級指的是寫鎖降級成為讀鎖。把持住當前擁有的寫鎖的同時,再獲取到讀鎖,隨後釋放寫鎖的過程。
寫鎖是執行緒獨佔,讀鎖是共享,所以寫->讀是升級。(讀->寫,是不能實現的)
示例3
// 將hashmap改造一個併發安全的
// 比hashTable的實現,效率高,讀取的適合並不會同步執行
public class MapDemo {
private final Map<String, Object> m = new HashMap<>();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public Object get(String key) {
r.lock(); // 可以同時多個執行緒獲取這把鎖
try {
return m.get(key);
} finally {
r.unlock();
}
}
public Object[] allKeys() {
r.lock();
try {
return m.keySet().toArray();
} finally {
r.unlock();
}
}
public Object put(String key, Object value) {
w.lock(); // 一個執行緒獲取 這把鎖
try {
return m.put(key, value);
} finally {
w.unlock();
}
}
public void clear() {
w.lock();
try {
m.clear();
} finally {
w.unlock();
}
}
}
對於Hashtable而言
Hashtable原始碼:
package java.util;
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
private transient Entry<?,?>[] table;
private transient int count;
private int threshold;
private float loadFactor;
private transient int modCount = 0;
public Hashtable(int initialCapacity, float loadFactor) {
//
}
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
public Hashtable() {
//
}
public Hashtable(Map<? extends K, ? extends V> t) {
//
}
public synchronized int size() {
//
}
public synchronized boolean isEmpty() {
//
}
public synchronized Enumeration<K> keys() {
//
}
public synchronized Enumeration<V> elements() {
//
}
public synchronized boolean contains(Object value) {
//
}
public boolean containsValue(Object value) {
//
}
public synchronized boolean containsKey(Object key) {
//
}
@SuppressWarnings("unchecked")
public synchronized V get(Object key) {
//
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
@SuppressWarnings("unchecked")
protected void rehash() {
//
}
private void addEntry(int hash, K key, V value, int index) {
//
}
public synchronized V put(K key, V value) {
//
}
public synchronized V remove(Object key) {
//
}
public synchronized void putAll(Map<? extends K, ? extends V> t) {
//
}
public synchronized void clear() {
//
}
public synchronized Object clone() {
//
}
public synchronized String toString() {
//
}
private <T> Enumeration<T> getEnumeration(int type) {
//
}
private <T> Iterator<T> getIterator(int type) {
//
}
private transient volatile Set<K> keySet;
private transient volatile Set<Map.Entry<K,V>> entrySet;
private transient volatile Collection<V> values;
public Set<K> keySet() {
//
}
private class KeySet extends AbstractSet<K> {
//
}
public Set<Map.Entry<K,V>> entrySet() {
//
}
private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
//
}
public Collection<V> values() {
//
}
private class ValueCollection extends AbstractCollection<V> {
//
}
public synchronized boolean equals(Object o) {
//
}
public synchronized int hashCode() {
//
}
@Override
public synchronized V getOrDefault(Object key, V defaultValue) {
//
}
@SuppressWarnings("unchecked")
@Override
public synchronized void forEach(BiConsumer<? super K, ? super V> action) {
//
}
@SuppressWarnings("unchecked")
@Override
public synchronized void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
//
}
@Override
public synchronized V putIfAbsent(K key, V value) {
//
}
@Override
public synchronized boolean remove(Object key, Object value) {
//
}
@Override
public synchronized boolean replace(K key, V oldValue, V newValue) {
//
}
@Override
public synchronized V replace(K key, V value) {
//
}
@Override
public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
//
}
@Override
public synchronized V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
//
}
@Override
public synchronized V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
//
}
@Override
public synchronized V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
//
}
private void writeObject(java.io.ObjectOutputStream s)
//
//
}
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
//
}
private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
throws StreamCorruptedException
{
//
}
private static class Entry<K,V> implements Map.Entry<K,V> {
//
}
private static final int KEYS = 0;
private static final int VALUES = 1;
private static final int ENTRIES = 2;
private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
Entry<?,?>[] table = Hashtable.this.table;
int index = table.length;
Entry<?,?> entry;
Entry<?,?> lastReturned;
int type;
boolean iterator;
protected int expectedModCount = modCount;
Enumerator(int type, boolean iterator) {
this.type = type;
this.iterator = iterator;
}
public boolean hasMoreElements() {
//
}
@SuppressWarnings("unchecked")
public T nextElement() {
//
}
public boolean hasNext() {
//
}
public T next() {
//
}
public void remove() {
//
synchronized(Hashtable.this) {
//
}
}
}
}
可以看到大部分的實現的都是對方法進行上鎖來解決執行緒的高併發
示例4
讀寫高併發
使用synchronized關鍵字
// 不用讀寫鎖
public class ReentrantReadWriteLockDemo1 {
public static void main(String[] args) {
final ReentrantReadWriteLockDemo1 readWriteLockDemo1 = new ReentrantReadWriteLockDemo1();
// 多執行緒同時讀/寫
new Thread(() -> {
readWriteLockDemo1.read(Thread.currentThread());
}).start();
new Thread(() -> {
readWriteLockDemo1.write(Thread.currentThread());
}).start();
new Thread(() -> {
readWriteLockDemo1.read(Thread.currentThread());
}).start();
}
// 不管讀寫,只有一個執行緒能用, 獨享鎖
public synchronized void read(Thread thread) { // 2秒
long start = System.currentTimeMillis();
while(System.currentTimeMillis() - start <= 1) {
System.out.println(thread.getName()+"正在進行“讀”操作");
}
System.out.println(thread.getName()+"“讀”操作完畢");
}
/** 寫 */
public synchronized void write(Thread thread) {
long start = System.currentTimeMillis();
while(System.currentTimeMillis() - start <= 1) {
System.out.println(thread.getName()+"正在進行“寫”操作");
}
System.out.println(thread.getName()+"“寫”操作完畢");
}
}
使用ReentrantReadWriteLock鎖實現
package icu.shaoyayu.multithreading.chapter4;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author shaoyayu
* @E_Mail
* @Version 1.0.0
* @readme :
*/
// 讀寫鎖(既保證了讀資料的效率,也保證資料的一致性)
public class ReentrantReadWriteLockDemo2 {
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public static void main(String[] args) {
final ReentrantReadWriteLockDemo2 readWriteLockDemo2 = new ReentrantReadWriteLockDemo2();
// 多執行緒同時讀/寫
new Thread(() -> {
readWriteLockDemo2.read(Thread.currentThread());
}).start();
new Thread(() -> {
readWriteLockDemo2.read(Thread.currentThread());
}).start();
new Thread(() -> {
readWriteLockDemo2.write(Thread.currentThread());
}).start();
}
// 多執行緒讀,共享鎖
public void read(Thread thread) {
readWriteLock.readLock().lock();
try {
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start <= 1) {
System.out.println(thread.getName() + "正在進行“讀”操作");
}
System.out.println(thread.getName() + "“讀”操作完畢");
} finally {
readWriteLock.readLock().unlock();
}
}
/**
* 寫
*/
public void write(Thread thread) {
readWriteLock.writeLock().lock();
try {
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start <= 1) {
System.out.println(thread.getName() + "正在進行“寫”操作");
}
System.out.println(thread.getName() + "“寫”操作完畢");
} finally {
readWriteLock.writeLock().unlock();
}
}
}
示例5
快取示例
// 快取示例
public class CacheDataDemo {
// 建立一個map用於快取
private Map<String, Object> map = new HashMap<>();
private static ReadWriteLock rwl = new ReentrantReadWriteLock();
public static void main(String[] args) {
// 1 讀取快取裡面的資料
// cache.query()
// 2 如果換成沒資料,則取資料庫裡面查詢 database.query()
// 3 查詢完成之後,資料塞到塞到快取裡面 cache.put(data)
}
public Object get(String id) {
Object value = null;
// 首先開啟讀鎖,從快取中去取
rwl.readLock().lock();
try {
if (map.get(id) == null) {
// TODO database.query(); 全部查詢資料庫 ,快取雪崩
// 必須釋放讀鎖
rwl.readLock().unlock();
// 如果快取中沒有釋放讀鎖,上寫鎖。如果不加鎖,所有請求全部去查詢資料庫,就崩潰了
rwl.writeLock().lock(); // 所有執行緒在此處等待 1000 1 999 (在同步程式碼裡面再次檢查是否快取)
try {
// 雙重檢查,防止已經有執行緒改變了當前的值,從而出現重複處理的情況
if (map.get(id) == null) {
// TODO value = ...如果快取沒有,就去資料庫裡面讀取
}
rwl.readLock().lock(); // 加讀鎖降級寫鎖,這樣就不會有其他執行緒能夠改這個值,保證了資料一致性
} finally {
rwl.writeLock().unlock(); // 釋放寫鎖@
}
}
} finally {
rwl.readLock().unlock();
}
return value;
}
}
問題滯留,異常的時候,釋放需要釋放那些鎖才方便
Condition
用於替代wait/notifyo
Object中的wait(),notify(),notifyAll()方法是和synchronized配合使用的,可以喚醒一個或者全部(單個等待集);
Condition是需要與Lock配合使用的,提供多個等待集合,更精確的控制(底層是park/unpark機制);
示例6
// condition 實現佇列執行緒安全。
public class QueueDemo {
final Lock lock = new ReentrantLock();
// 指定條件的等待 - 等待有空位
final Condition notFull = lock.newCondition();
// 指定條件的等待 - 等待不為空
final Condition notEmpty = lock.newCondition();
// 定義陣列儲存資料
final Object[] items = new Object[100];
int putptr, takeptr, count;
// 寫入資料的執行緒,寫入進來
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length) // 資料寫滿了
{
notFull.await(); // 寫入資料的執行緒,進入阻塞
}
items[putptr] = x;
if (++putptr == items.length) {
putptr = 0;
}
++count;
notEmpty.signal(); // 喚醒指定的讀取執行緒
} finally {
lock.unlock();
}
}
// 讀取資料的執行緒,呼叫take
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
notEmpty.await(); // 執行緒阻塞在這裡,等待被喚醒
}
Object x = items[takeptr];
if (++takeptr == items.length) {
takeptr = 0;
}
--count;
notFull.signal(); // 通知寫入資料的執行緒,告訴他們取走了資料,繼續寫入
return x;
} finally {
lock.unlock();
}
}
}