<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## 23 按需上鎖—ReadWriteLock詳解 > 合理安排時間,就等于節約時間。 > ——培根 前文我們分析了 java.util.concurrent.locks 包下的 Lock 接口和它的實現 ReentrantLock。ReentrantLock 是一種顯式鎖,提供更為高級的功能,但需要顯式的上鎖和解鎖。ReentrantLock 也是互斥鎖,有如下三種互斥情況:讀/寫、寫/寫、讀/讀。ReentrantLock 的加鎖策略是保守的,任意操作都需要先加鎖才可以.但其實在某些情況下,我們只需要控制“讀/寫”和“寫/寫”這兩種互斥情況。一般情況下,絕大多數程序中讀操作比例更大。其實讀操作之間并不需要互斥,因為讀的需求是讀到最新的數據,并且在讀的同時不要有其它線程修改數據即可。那我們需要的鎖是“讀/寫”互斥,而“讀/讀”并不互斥。這樣的好處是,讀操作可以并發進行,減少了互斥的情況,能夠提升程序的性能。打個比方,我們去旅游,在觀景臺大家可以一塊欣賞風景,這就是 “讀/讀” 并不互斥,大家可以在觀景臺一塊看。但是假如在觀景臺有個望遠鏡,需要使用望遠鏡才能看清。那么就變成了 “讀/讀” 互斥,人多的話顯然會造成排隊現象。 ![圖片描述](https://img.mukewang.com/5dc37ef8000162e908460514.jpg) 顯然 ReentrantLock 和 synchronized 是做不到這一點的,本節我們會介紹一種新的鎖—讀寫鎖 ReadWriteLock。 ## 1、ReadWriteLock 簡介 ReadWriteLock 為我們提供了讀寫之間不同互斥策略的鎖。因此,在某些情況下,他能夠帶來更好的性能。一般來說,假如你的程序有頻繁的讀操作,那么ReadWriteLock可能會為你帶來性能的提升。但是由于讀寫控制的策略不一樣,帶來了鎖內部的復雜度。所以如果你程序的讀操作并沒有達到一定數量,反而使用讀寫鎖會比互斥鎖性能更差。 因此 ReadWriteLock 是一種提升性能的手段,但不一定奏效。我們的程序可以嘗試使用它來調節性能,如果發現沒有效果或者更差,也可以很方便的換回互斥鎖。 ReadWriteLock 顧名思義讀寫鎖,也就是說同一個鎖對讀和寫的上鎖方式是不一樣的寫鎖的互斥性更高。這里我們來看看鎖降級和升級的概念。 **鎖降級** 如果線程持有寫鎖,如果可以在不釋放寫鎖的情況下,獲取讀鎖,這就是鎖降級。ReadWriteLock 是支持鎖降級的。 **鎖升級** 如果線程持有讀鎖,那么他是否可以不釋放讀鎖,直接獲取寫鎖。這意味著從一個低級別的鎖升級到高級別的鎖。其實就是變相的插隊,無視其它在排隊等待寫鎖的線程。ReadWriteLock 并不支持鎖升級。 以上兩種概念我們可以通過寫段代碼來體驗下。 代碼一: ~~~java public class Client { public static void main(String[] args) throws InterruptedException { ReadWriteLock lock = new ReentrantReadWriteLock(); Lock readLock = lock.readLock(); Lock writeLock = lock.writeLock(); writeLock.lock(); System.out.println("got the write lock"); readLock.lock(); System.out.println("got the read lock"); } } ~~~ 輸出: ~~~java got the write lock got the read lock ~~~ 代碼二: ~~~java public class Client { public static void main(String[] args) throws InterruptedException { ReadWriteLock lock = new ReentrantReadWriteLock(); Lock readLock = lock.readLock(); Lock writeLock = lock.writeLock(); readLock.lock(); System.out.println("got the read lock"); writeLock.lock(); System.out.println("got the write lock"); } } ~~~ 輸出: ~~~java got the read lock ~~~ 第一段代碼中,我們可以在獲取寫鎖后,再次成功獲得讀鎖。而代碼 2 中,我們在獲取讀鎖后試圖去獲取寫鎖。這樣會使得程序阻塞在 readLock.lock()。由于 writeLock 沒有機會 unlock,就形成了死鎖。 ## 2、ReadWriteLock使用 ReadWriteLock 使用起來其實很簡單,和 Lock 基本一致。我們使用它主要是為了對性能進行優化。我們通過下面的例子,一是熟悉它的使用,二來也可以測試下它對性能的優化效果。 首先我們看使用 Lock 的情況: ~~~java public class LockExample { String myName; ReentrantLock lock = new ReentrantLock(); public void printMyName() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "My name is " + myName); Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void setMyName() { lock.lock(); try { myName=Thread.currentThread().getName(); System.out.println(Thread.currentThread().getName() + "set my name to " + myName); } finally { lock.unlock(); } } public static void main(String[] args) { Long startTime = new Date().getTime(); LockExample example = new LockExample(); new Thread(()->{ while (true){ example.setMyName(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(()->{ while (true){ example.setMyName(); try { Thread.sleep(new Random().nextInt(10)); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); IntStream.range(0,100).forEach(num->{ new Thread(()->{ example.printMyName(); System.out.println("NO. "+num+" reader finished. Time passed: "+(new Date().getTime()-startTime)); }).start(); }); } } ~~~ 以上代碼我們啟動了兩個線程不斷寫入,每次間隔 10ms, 以讓其它線程能夠獲取到鎖。另外有 100 個讀線程 ,每次讀取完成,睡眠 10ms,目的是延遲讀鎖的釋放。由于使用了排他鎖,所以讀取操作間是互斥的,每次讀取都要等 10ms 釋放鎖后,其它線程才能讀取。那么 100 次讀取就至少花費了 100\*10 = 1000ms。再加上其它消耗,所以最終全部讀取線程完成工作的時候,過去了 1182ms。輸出如下: ~~~ NO. 96 reader finished. Time passed: 1148 Thread-99My name is Thread-0 NO. 97 reader finished. Time passed: 1160 Thread-100My name is Thread-0 NO. 98 reader finished. Time passed: 1172 Thread-101My name is Thread-0 NO. 99 reader finished. Time passed: 1182 ~~~ 接下來我們改造下程序,改為 ReadWriteLock,代碼如下: ~~~java public class ReadWriteLockExample { String myName; ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public void printMyName() { lock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + "My name is " + myName); Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.readLock().unlock(); } } public void setMyName() { lock.writeLock().lock(); try { myName=Thread.currentThread().getName(); System.out.println(Thread.currentThread().getName() + "set my name to " + myName); } finally { lock.writeLock().unlock(); } } public static void main(String[] args) { Long startTime = new Date().getTime(); ReadWriteLockExample example = new ReadWriteLockExample(); new Thread(()->{ while (true){ example.setMyName(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(()->{ while (true){ example.setMyName(); try { Thread.sleep(new Random().nextInt(10)); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); IntStream.range(0,100).forEach(num->{ new Thread(()->{ example.printMyName(); System.out.println("NO. "+num+" reader finished. Time passed: "+(new Date().getTime()-startTime)); }).start(); }); } } ~~~ 可以看到只是把鎖換成了 ReentrantReadWriteLock,然后 printMyName 中使用讀鎖,setMyName 中使用寫鎖。運行后輸出如下: ~~~ NO. 92 reader finished. Time passed: 97 NO. 96 reader finished. Time passed: 97 Thread-0set my name to Thread-0 NO. 98 reader finished. Time passed: 97 Thread-1set my name to Thread-1 NO. 99 reader finished. Time passed: 97 ~~~ 最后一個 reader 完成工作,只用了 97ms,對比起使用互斥鎖的 1182ms,速度提升了 10倍以上!原因就是讀操作之間不會互斥,可以并發讀取。從而性能大幅度得到提升。 ## 3、ReadWriteLock 使用場景 從上面例子可以看出,如果讀操作遠遠多于寫操作,使用 ReadWriteLock 可以大幅提升性能。但如果是一個寫入密集型的程序,那么 ReadWriteLock 并不會帶來顯著性能的提升,因為即使使用 ReadWriteLock,“寫/寫”及“讀/寫”依舊是互斥的。并且由于要分開控制讀寫兩種鎖,還需要額外的開銷。 如果你的并發程序存在性能問題,可以把 ReadWriteLock 作為性能調優的手段,進行嘗試。究竟讀和寫的線程達到什么比例時,使用 ReadWriteLock 性能更好,其實并沒有定論。完全和你的程序場景有關系,所以使用 ReadWriteLock 做性能調優時,一定要基于實際的測試數據,而不是一股腦的全部使用 ReadWriteLock。 ## 4、ReadWriteLock 實現 關于 ReadWriteLock 的實現,我們先看下面的類圖: ![圖片描述](https://img.mukewang.com/5dc37eb40001705d08130325.jpg) 可以看到 ReentrantReadWriteLock 中持有 readerLock 和 writerLock 兩把鎖,而這兩把鎖也是 Lock 接口的實現。ReadLock 間由于是非互斥的,所以ReadLock對lock方法的實現如下: ~~~java public void lock() { sync.acquireShared(1); } ~~~ 而WriteLock對lock方法的實現則是如下: ~~~java public void lock() { sync.acquire(1); } ~~~ 可以看到 ReadLock 的 lock 方法中調用的是 acquireShared,也就是共享方式獲取鎖。兩者都是通過 sync 來實現,兩種鎖的 sync 對象都是來自 ReentrantReadWriteLock 的構造函數: ~~~java public ReentrantReadWriteLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); readerLock = new ReadLock(this); writerLock = new WriteLock(this); } ~~~ 而 Sync 之前我們已經講解過,它繼承自 AbstractQueuedSynchronizer,也就是 AQS。通過 AQS 提供的模版實現同步原語。而最終的實現方式則是在 AbstractQueuedSynchronizer 的子類,也就是 FairSync 和 NonfairSync 中。AQS 的原理之前已經講過。結合 AQS 的原理,再加上之前我們對 ReentrantLock 源代碼的分析,再來分析 ReentrantReadWriteLock 的源代碼,并不困難,大家可以自行繼續分析。 ## 5、總結 本節我們又學習了一種比較實用的鎖。ReentrantReadWriteLock 允許并發的讀,如果你的程序以讀取為主,那么使用 ReentrantReadWriteLock 會顯著提升你的性能。但如果場景不符合,不但不會提升性能,還會因為鎖的復雜度,反而降級性能。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看