ReentrantReadWriteLock是Lock的另外一種實現方式,咱們已經知道了ReentrantLock是一個排他鎖,同一時間只容許一個線程訪問,而ReentrantReadWriteLock容許多個讀線程同時訪問,但不容許寫線程和讀線程、寫線程和寫線程同時訪問。相對於排他鎖,提升了併發性。在實際應用中,大部分狀況下對共享數據(如緩存)的訪問都是讀操做遠多於寫操做,這時ReentrantReadWriteLock可以提供比排他鎖更好的併發性和吞吐量。java
讀寫鎖內部維護了兩個鎖,一個用於讀操做,一個用於寫操做。全部 ReadWriteLock實現都必須保證 writeLock操做的內存同步效果也要保持與相關 readLock的聯繫。也就是說,成功獲取讀鎖的線程會看到寫入鎖以前版本所作的全部更新。數據庫
ReentrantReadWriteLock支持如下功能:緩存
在緩存有效的狀況下,支持併發讀。緩存失效,只容許獨佔寫。微信
import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class HibernateCache { /* 定義一個Map來模擬緩存 */ private Map<String, Object> cache = new HashMap<String, Object>(); /* 建立一個讀寫鎖 */ private ReadWriteLock rwLock = new ReentrantReadWriteLock(); /** * 模擬Hibernate緩存,優先緩存,若緩存不存在寫鎖更新 * * @param key * @return */ public Object getData(String key) { /* 上讀鎖 */ rwLock.readLock().lock(); /* 定義從緩存中讀取的對象 */ Object value = null; try { /* 從緩存中讀取數據 */ value = cache.get(key); if (value == null) { /* 若是緩存中沒有數據,咱們就把讀鎖關閉,直接上寫鎖【讓一個線程去數據庫中取數據】 */ rwLock.readLock().unlock(); /* 上寫鎖 */ rwLock.writeLock().lock(); try { /* 上了寫鎖以後再判斷一次【咱們只讓一個線程去數據庫中取值便可,當第二個線程過來的時候,發現value不爲空了就去緩存中取值】 */ if (value == null) { /* 模擬去數據庫中取值 */ value = "hello"; System.out.println("修改換緩存"); cache.put(key, value); } } finally { /* 寫完以後把寫鎖關閉 */ rwLock.writeLock().unlock(); } /* 緩存中已經有了數據,咱們再把已經 關閉的讀鎖打開 */ rwLock.readLock().lock(); } return value; } finally { /* 最後把讀鎖也關閉 */ rwLock.readLock().unlock(); } } public Map<String, Object> getCache() { return cache; } public void setCache(Map<String, Object> cache) { this.cache = cache; } }
當一份共享數據只能一個西安測繪給你寫數據,能夠多個線程讀數據。能夠選擇讀寫鎖,支持併發讀,獨佔寫,提升併發。併發
代碼以下:dom
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWrite { private ReadWrite() { } private static class singleFactory { private static final ReadWrite INSTANCE = new ReadWrite(); } public static ReadWrite getInstance() { return singleFactory.INSTANCE; } /* 共享數據,只能一個線程寫數據,能夠多個線程讀數據 */ private Object data = null; /* 建立一個讀寫鎖 */ ReadWriteLock rwlock = new ReentrantReadWriteLock(); /** * 讀數據,能夠多個線程同時讀, 因此上讀鎖便可 */ public void get() { /* 上讀鎖 */ rwlock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 準備讀數據!"); /* 休眠 */ Thread.sleep((long) (Math.random() * 1000)); System.out.println(Thread.currentThread().getName() + "讀出的數據爲 :" + data); } catch (InterruptedException e) { e.printStackTrace(); } finally { rwlock.readLock().unlock(); } } /** * 寫數據,多個線程不能同時 寫 因此必須上寫鎖 * * @param data */ public void put(Object data) { /* 上寫鎖 */ rwlock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 準備寫數據!"); /* 休眠 */ Thread.sleep((long) (Math.random() * 1000)); this.data = data; System.out.println(Thread.currentThread().getName() + " 寫入的數據: " + data); } catch (Exception e) { e.printStackTrace(); } finally { rwlock.writeLock().unlock(); } } }
單元測試socket
public class LockTest { public static void main(String[] args) { ReadWrite readWrite = ReadWrite.getInstance(); for (int i = 0; i < 8; i++) { /* 建立並啓動8個讀線程 */ new Thread(() -> readWrite.get()).start(); /*建立8個寫線程*/ new Thread(() -> readWrite.put(new Random().nextInt(8))).start(); } } }
運行結果:高併發
Thread-0讀出的數據爲 :null Thread-1 準備寫數據! Thread-1 寫入的數據: 6 Thread-3 準備寫數據! Thread-3 寫入的數據: 4 Thread-4 準備讀數據! Thread-2 準備讀數據! Thread-2讀出的數據爲 :4 Thread-4讀出的數據爲 :4 Thread-5 準備寫數據! Thread-5 寫入的數據: 1 Thread-6 準備讀數據! Thread-6讀出的數據爲 :1 Thread-7 準備寫數據! Thread-7 寫入的數據: 6 Thread-8 準備讀數據! Thread-8讀出的數據爲 :6 Thread-9 準備寫數據! Thread-9 寫入的數據: 4 Thread-10 準備讀數據! Thread-10讀出的數據爲 :4 Thread-11 準備寫數據! Thread-11 寫入的數據: 4 Thread-12 準備讀數據! Thread-12讀出的數據爲 :4 Thread-13 準備寫數據! Thread-13 寫入的數據: 6 Thread-14 準備讀數據! Thread-14讀出的數據爲 :6 Thread-15 準備寫數據! Disconnected from the target VM, address: '127.0.0.1:55431', transport: 'socket' Thread-15 寫入的數據: 0
這裏會有一個規律:獲取了寫鎖後數據必須從準備寫數據到寫入數據一鼓作氣,也就是原子操做,線程獨佔。單元測試
而讀鎖的狀況下可有多個線程準備讀,多個線程同時讀出數據。測試
關注微信公衆號JavaStorm獲取最新文章。