日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java线程安全Lock、ReentrantLock、ReentrantReadWriteLock

發布時間:2024/9/30 java 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java线程安全Lock、ReentrantLock、ReentrantReadWriteLock 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/120750932
本文出自【趙彥軍的博客】

Java線程安全StampedLock
Java線程安全Lock、ReentrantLock、ReentrantReadWriteLock
Java線程安全集合總結
Java原子操作Atomic

文章目錄

  • 前言
  • Lock
  • ReentrantLock
    • 公平鎖/非公平鎖
    • 超時機制
    • 可重入鎖
  • 讀寫鎖 ReentrantReadWriteLock
    • 源碼結構
    • 總結
    • 示例

前言

java5之后,并發包中新增了Lock接口(以及相關實現類)用來實現鎖的功能,它提供了與synchronized關鍵字類似的同步功能。

既然有了synchronized這種內置的鎖功能,為何要新增Lock接口?先來想象一個場景:手把手的進行鎖獲取和釋放,先獲得鎖A,然后再獲取鎖B,當獲取鎖B后釋放鎖A同時獲取鎖C,當鎖C獲取后,再釋放鎖B同時獲取鎖D,以此類推,這種場景下,synchronized關鍵字就不那么容易實現了,而使用Lock卻顯得容易許多。

Lock

Lock 是一個接口

public interface Lock {void lock();void lockInterruptibly() throws InterruptedException;boolean tryLock();boolean tryLock(long time, TimeUnit unit) throws InterruptedException;void unlock();Condition newCondition(); }

ReentrantLock

public class ReentrantLock implements Lock, java.io.Serializable { }

使用如下:

public class LockTest {Lock lock = new ReentrantLock();public void run(){//獲取鎖 lock.lock();//doSomething//釋放鎖lock.unlock();} }

使用起來,也非常簡單,首先 lock.lock(); 獲取鎖,然后去執行自己的邏輯,最后調用 lock.unlock(); 釋放鎖。

但是這里有個問題,如果我們的邏輯發生異常,那就永遠無法釋放鎖,所以我們優化一下,把釋放鎖的邏輯放在 finally 塊中,如下

public class LockTest {Lock lock = new ReentrantLock();public void run(){//獲取鎖lock.lock();try {//doSomething}catch (Exception e){}finally {//釋放鎖lock.unlock();}} }

公平鎖/非公平鎖

  • 非公平鎖:如果同時還有另一個線程進來嘗試獲取,那么有可能會讓這個線程搶先獲取;

  • 公平鎖:如果同時還有另一個線程進來嘗試獲取,當它發現自己不是在隊首的話,就會排到隊尾,由隊首的線程獲取到鎖。

ReentrantLock 主要利用CAS+AQS隊列來實現。它支持公平鎖和非公平鎖,兩者的實現類似。

  • CAS:Compare and Swap,比較并交換。CAS有3個操作數:內存值V、預期值A、要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改為B,否則什么都不做。該操作是一個原子操作,被廣泛的應用在Java的底層實現中。在Java中,CAS主要是由sun.misc.Unsafe這個類通過JNI調用CPU底層指令實現

ReentrantLock主要利用CAS+AQS隊列來實現。它支持公平鎖和非公平鎖,兩者的實現類似。

AbstractQueuedSynchronizer 簡稱AQS

我們來看一下 ReentrantLock 構造函數

public ReentrantLock() {sync = new NonfairSync();}/*** Creates an instance of {@code ReentrantLock} with the* given fairness policy.** @param fair {@code true} if this lock should use a fair ordering policy*/public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}

構造支持傳入 fair 來指定是否是公平鎖

  • FairSync() 公平鎖
  • NonfairSync() 非公平鎖

如果使用無參構造函數,則是非公平鎖。

超時機制

在 ReetrantLock的tryLock(long timeout, TimeUnit unit) 提供了超時獲取鎖的功能。它的語義是在指定的時間內如果獲取到鎖就返回true,獲取不到則返回false。這種機制避免了線程無限期的等待鎖釋放。

可重入鎖

  • 可重入鎖??芍厝腈i是指同一個線程可以多次獲取同一把鎖。ReentrantLock和synchronized都是可重入鎖。

  • 可中斷鎖。可中斷鎖是指線程嘗試獲取鎖的過程中,是否可以響應中斷。synchronized是不可中斷鎖,而ReentrantLock則提供了中斷功能。

  • 公平鎖與非公平鎖。公平鎖是指多個線程同時嘗試獲取同一把鎖時,獲取鎖的順序按照線程達到的順序,而非公平鎖則允許線程“插隊”。synchronized是非公平鎖,而ReentrantLock的默認實現是非公平鎖,但是也可以設置為公平鎖。

讀寫鎖 ReentrantReadWriteLock

現實中有這樣一種場景:對共享資源有讀和寫的操作,且寫操作沒有讀操作那么頻繁。

在沒有寫操作的時候,多個線程同時讀一個資源沒有任何問題,所以應該允許多個線程同時讀取共享資源;但是如果一個線程想去寫這些共享資源,就不應該允許其他線程對該資源進行讀和寫的操作了。

針對這種場景,JAVA的并發包提供了讀寫鎖 ReentrantReadWriteLock,它表示兩個鎖,一個是讀操作相關的鎖,稱為共享鎖;一個是寫相關的鎖,稱為排他鎖,描述如下:

線程進入讀鎖的前提條件:

  • 沒有其他線程的寫鎖,

  • 沒有寫請求或者有寫請求,但調用線程和持有鎖的線程是同一個。

線程進入寫鎖的前提條件:

  • 沒有其他線程的讀鎖

  • 沒有其他線程的寫鎖

而讀寫鎖有以下三個重要的特性:

(1)公平選擇性:支持非公平(默認)和公平的鎖獲取方式,吞吐量還是非公平優于公平。

(2)重進入:讀鎖和寫鎖都支持線程重進入。

(3)鎖降級:遵循獲取寫鎖、獲取讀鎖再釋放寫鎖的次序,寫鎖能夠降級成為讀鎖。

源碼結構

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {/** 讀鎖 */private final ReentrantReadWriteLock.ReadLock readerLock;/** 寫鎖 */private final ReentrantReadWriteLock.WriteLock writerLock;final Sync sync;/** 使用默認(非公平)的排序屬性創建一個新的 ReentrantReadWriteLock */public ReentrantReadWriteLock() {this(false);}/** 使用給定的公平策略創建一個新的 ReentrantReadWriteLock */public ReentrantReadWriteLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();readerLock = new ReadLock(this);writerLock = new WriteLock(this);}/** 返回用于寫入操作的鎖 */public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }/** 返回用于讀取操作的鎖 */public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }abstract static class Sync extends AbstractQueuedSynchronizer {}static final class NonfairSync extends Sync {}static final class FairSync extends Sync {}public static class ReadLock implements Lock, java.io.Serializable {}public static class WriteLock implements Lock, java.io.Serializable {} }

1、類的繼承關系

public class ReentrantReadWriteLockimplements ReadWriteLock, java.io.Serializable {}

說明:可以看到,ReentrantReadWriteLock實現了ReadWriteLock接口,ReadWriteLock接口定義了獲取讀鎖和寫鎖的規范,具體需要實現類去實現;同時其還實現了Serializable接口,表示可以進行序列化,在源代碼中可以看到ReentrantReadWriteLock實現了自己的序列化邏輯。

2、ReentrantReadWriteLock有五個內部類,五個內部類之間也是相互關聯的。內部類的關系如下圖所示。


說明:如上圖所示,Sync繼承自AQS、NonfairSync繼承自Sync類、FairSync繼承自Sync類(通過構造函數傳入的布爾值決定要構造哪一種Sync實例);ReadLock實現了Lock接口、WriteLock也實現了Lock接口。

總結

  • 在線程持有讀鎖的情況下,該線程不能取得寫鎖(因為獲取寫鎖的時候,如果發現當前的讀鎖被占用,就馬上獲取失敗,不管讀鎖是不是被當前線程持有)。

  • 在線程持有寫鎖的情況下,該線程可以繼續獲取讀鎖(獲取讀鎖時如果發現寫鎖被占用,只有寫鎖沒有被當前線程占用的情況才會獲取失敗)。

仔細想想,這個設計是合理的:因為當線程獲取讀鎖的時候,可能有其他線程同時也在持有讀鎖,因此不能把獲取讀鎖的線程“升級”為寫鎖;而對于獲得寫鎖的線程,它一定獨占了讀寫鎖,因此可以繼續讓它獲取讀鎖,當它同時獲取了寫鎖和讀鎖后,還可以先釋放寫鎖繼續持有讀鎖,這樣一個寫鎖就“降級”為了讀鎖。

綜上:

一個線程要想同時持有寫鎖和讀鎖,必須先獲取寫鎖再獲取讀鎖;寫鎖可以“降級”為讀鎖;讀鎖不能“升級”為寫鎖。

示例

public class LockTest {ReadWriteLock lock = new ReentrantReadWriteLock();Lock readLock = lock.readLock();Lock writeLock = lock.writeLock();}

總結

以上是生活随笔為你收集整理的Java线程安全Lock、ReentrantLock、ReentrantReadWriteLock的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。