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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Lock接口Condition,以及Lock与synchronized异同

發(fā)布時(shí)間:2025/3/15 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Lock接口Condition,以及Lock与synchronized异同 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、synchronized與Lock

基于synchronized關(guān)鍵字去實(shí)現(xiàn)同步的,(jvm內(nèi)置鎖,也叫隱式鎖,由我們的jvm自動(dòng)去加鎖跟解鎖的)juc下的基于Lock接口的這樣的一種鎖的實(shí)現(xiàn)方式(顯式鎖,他需要我們手動(dòng)的去加鎖,跟解鎖)

一張圖了解一下區(qū)別:

二、Lock接口的實(shí)現(xiàn)


Lock鎖實(shí)現(xiàn)提供了比使用同步方法和語句可以獲得的更廣泛的鎖操作。它們允許更靈活的結(jié)構(gòu),可能具有非常不同的屬性,并且可能支持多個(gè)關(guān)聯(lián)的條件對象。

Lock l = ...;l.lock();try {// access the resource protected by this lock} finally {l.unlock();}

三、Lock實(shí)現(xiàn)ReentrantLock

由英文知道:ReentrantLock稱是重入鎖,可以反復(fù)得到相同的一把鎖,它有一個(gè)與鎖相關(guān)的獲取計(jì)數(shù)器,如果擁有鎖的某個(gè)線程再次得到鎖,那么獲取計(jì)數(shù)器就加1,然后鎖需要被釋放兩次才能獲得真正釋放(重入鎖)。圍繞著去實(shí)現(xiàn)的核心實(shí)質(zhì)就是AQS(AbstractQueuedSynchronizer)(抽象隊(duì)列同步器)ReentrantLock和synchronized關(guān)鍵字一樣可以用來實(shí)現(xiàn)線程之間的同步互斥,但是在功能是比synchronized關(guān)鍵字更強(qiáng)大而且更靈活。

ReentrantLock類常見方法(Lock接口已有方法這里沒加上):

class X {private final ReentrantLock lock = new ReentrantLock();// ...public void m() {lock.lock(); // block until condition holdstry {// ... method body} finally {lock.unlock()}}}

例子:

public class ReentrantLockTest extends Thread {public static ReentrantLock lock = new ReentrantLock();public static int i = 0;public ReentrantLockTest(String name) {super.setName(name);}@Overridepublic void run() {for (int j = 0; j < 100; j++) {lock.lock();try {System.out.println(this.getName() + " " + i);i++;} finally {lock.unlock();}}}/*** @param args* @throws InterruptedException*/public static void main(String[] args) throws InterruptedException {ReentrantLockTest test1 = new ReentrantLockTest("thread1");ReentrantLockTest test2 = new ReentrantLockTest("thread2");test1.start();test2.start();test1.join();test2.join();System.out.println(i);} } //以上例子,如果沒加lock會(huì)輸出一個(gè)小于200的數(shù)

注意:ReentrantLock并不是一種替代內(nèi)置加鎖的方法,而是作為一種可選擇的高級(jí)功能。相比于synchronized,ReentrantLock在功能上更加豐富,它具有可重入、可中斷、可限時(shí)、公平鎖等特點(diǎn)。

以下是他和synchronized的對比:

  • 1.synchronized是獨(dú)占鎖,加鎖和解鎖的過程自動(dòng)進(jìn)行,易于操作,但不夠靈活。ReentrantLock也是獨(dú)占鎖,加鎖和解鎖的過程需要手動(dòng)進(jìn)行,不易操作,但非常靈活。
  • 2.synchronized可重入,因?yàn)榧渔i和解鎖自動(dòng)進(jìn)行,不必?fù)?dān)心最后是否釋放鎖;ReentrantLock也可重入,但加鎖和解鎖需要手動(dòng)進(jìn)行(其實(shí)這也是Lock的相比synchronized的優(yōu)點(diǎn),更加靈活),且次數(shù)需一樣,否則其他線程無法獲得鎖。
  • 3.synchronized不可響應(yīng)中斷,一個(gè)線程獲取不到鎖就一直等著;ReentrantLock可以相應(yīng)中斷。
  • 4.ReentrantLock可設(shè)置超時(shí)退出,不會(huì)永久等待構(gòu)成死鎖。

在synchronized和reentrantLock之間進(jìn)行選擇:
雖然reentrantLock在1.5和1.6的表現(xiàn)中,性能遠(yuǎn)好于synchronized,但還是提倡使用synchronized:

  • 1.reentrantLock忘記關(guān)鎖的話,很危險(xiǎn)
  • 2.未來更可能提升的是 synchronized的性能,而不是reentrantLock
  • 3.當(dāng)需要用到某些高級(jí)性能時(shí),才考慮reentrantLock,例如:可定時(shí)的,可輪詢的,可中斷的,公平隊(duì)列等

公平鎖與非公平鎖(AQS的原因)
AQS內(nèi)部維護(hù)著一個(gè)FIFO的隊(duì)列,即CLH隊(duì)列。AQS的同步機(jī)制就是依靠CLH隊(duì)列實(shí)現(xiàn)的。

  • 一般意義上的鎖是不公平的,不一定先來的線程能先得到鎖,后來的線程就后得到鎖。不公平的鎖可能會(huì)產(chǎn)生饑餓現(xiàn)象。
  • 公平鎖的意思就是,這個(gè)鎖能保證線程是先來的先得到鎖。雖然公平鎖不會(huì)產(chǎn)生饑餓現(xiàn)象,但是公平鎖的性能會(huì)比非公平鎖差很多。

公平鎖:

非公平鎖:

注意: 默認(rèn)情況下ReentranLock類使用的是非公平鎖,若要使用公平鎖,如下 :

public ReentrantLock(boolean fair) public static ReentrantLock fairLock = new ReentrantLock(true);

四、Condition接口

我們知道synchronized關(guān)鍵字與wait()和notify/notifyAll()方法相配合就可以實(shí)現(xiàn)等待/通知機(jī)制,借助于Condition接口與newCondition() 方法ReentrantLock類也可以實(shí)現(xiàn)。Condition是JDK1.5之后才有的,它具有很好的靈活性,比如可以實(shí)現(xiàn)多路通知功能也就是在一個(gè)Lock對象中可以創(chuàng)建多個(gè)Condition實(shí)例(即對象監(jiān)視器),線程對象可以注冊在指定的Condition中,從而可以有選擇性的進(jìn)行線程通知,在調(diào)度線程上更加靈活。 condition可以通俗的理解為條件隊(duì)列。當(dāng)一個(gè)線程在調(diào)用了await方法以后,直到線程等待的某個(gè)條件為真的時(shí)候才會(huì)被喚醒。這種方式為線程提供了更加簡單的等待/通知模式。Condition必須要配合鎖一起使用,因?yàn)閷蚕頎顟B(tài)變量的訪問發(fā)生在多線程環(huán)境下。一個(gè)Condition的實(shí)例必須與一個(gè)Lock綁定,因此Condition一般都是作為Lock的內(nèi)部實(shí)現(xiàn)。

在使用notify/notifyAll()方法進(jìn)行通知時(shí),被通知的線程是由JVM選擇的,使用ReentrantLock類結(jié)合Condition實(shí)例可以實(shí)現(xiàn)“選擇性通知”,這個(gè)功能非常重要,而且是Condition接口默認(rèn)提供的。而synchronized關(guān)鍵字就相當(dāng)于整個(gè)Lock對象中只有一個(gè)Condition實(shí)例,所有的線程都注冊在它一個(gè)身上。如果執(zhí)行notifyAll()方法的話就會(huì)通知所有處于等待狀態(tài)的線程這樣會(huì)造成很大的效率問題,而Condition實(shí)例的signalAll()方法 只會(huì)喚醒注冊在該Condition實(shí)例中的所有等待線程。

class ShareDataOne {private int number = 0;private Lock lock = new ReentrantLock();private Condition cd = lock.newCondition();public void incr() throws InterruptedException {lock.lock();try {while (number != 0) {cd.await();}number++;System.out.println(Thread.currentThread().getName() + "\t" + number);cd.signalAll();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void decr() throws InterruptedException {lock.lock();try {while (number != 1) {cd.await();}number--;System.out.println(Thread.currentThread().getName() + "\t" + number);cd.signalAll();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}} } /*** 現(xiàn)在兩個(gè)線程* 操作一個(gè)初始值為0的變量* 實(shí)現(xiàn)一個(gè)線程對變量增加1,一個(gè)線程對變量減少1* 交替10輪* 、線程 操作 資源類 2、高內(nèi)聚低耦合* 1、判斷* 2、干活* 3、通知* 注意多線程之間的虛假喚醒*/ public class NotifyWaitDemo {public static void main(String[] args) {ShareDataOne shareDataOne = new ShareDataOne();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {shareDataOne.incr();} catch (InterruptedException e) {e.printStackTrace();}}}, "AA").start();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {shareDataOne.decr();} catch (InterruptedException e) {e.printStackTrace();}}}, "BB").start();} }

簡單地說:一個(gè)線程運(yùn)行完之后通過condition.signal()或者condition.signalAll()方法通知下一個(gè)特定的運(yùn)行,就這樣循環(huán)往復(fù)即可。

五、ReadWriteLock接口的實(shí)現(xiàn)類:ReentrantReadWriteLock

ReentrantLock(排他鎖)具有完全互斥排他的效果,即同一時(shí)刻只允許一個(gè)線程訪問,這樣做雖然雖然保證了實(shí)例變量的線程安全性,但效率非常低下。ReadWriteLock接口的實(shí)現(xiàn)類ReentrantReadWriteLock讀寫鎖就是為了解決這個(gè)問題。讀寫鎖維護(hù)了兩個(gè)鎖,一個(gè)是讀操作相關(guān)的鎖也成為共享鎖,一個(gè)是寫操作相關(guān)的鎖也稱為排他鎖。通過分離讀鎖和寫鎖,其并發(fā)性比一般排他鎖有了很大提升。多個(gè)讀鎖之間不互斥,讀鎖與寫鎖互斥,寫鎖與寫鎖互斥(只要出現(xiàn)寫操作的過程就是互斥的。)。在沒有線程Thread進(jìn)行寫入操作時(shí),進(jìn)行讀取操作的多個(gè)Thread都可以獲取讀鎖,而進(jìn)行寫入操作的Thread只有在獲取寫鎖后才能進(jìn)行寫入操作。即多個(gè)Thread可以同時(shí)進(jìn)行讀取操作,但是同一時(shí)刻只允許一個(gè)Thread進(jìn)行寫入操作。

ReentrantReadWriteLock的特性:

ReentrantReadWriteLock常見方法: 構(gòu)造方法

問題例子:

class MyCache{private volatile Map<String,Object> map = new HashMap<>();public void put(String key,Object value){System.out.println(Thread.currentThread().getName()+"\t 正在寫"+key);//暫停一會(huì)兒線程try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace(); }map.put(key,value);System.out.println(Thread.currentThread().getName()+"\t 寫完了"+key);}public Object get(String key){Object result = null;System.out.println(Thread.currentThread().getName()+"\t 正在讀"+key);try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace(); }result = map.get(key);System.out.println(Thread.currentThread().getName()+"\t 讀完了"+result);return result;} } public class ReadWriteLockDemo {public static void main(String[] args) {MyCache myCache = new MyCache();for (int i = 1; i <= 3; i++) {final int num = i;new Thread(()->{myCache.put(num+"",num+"");},String.valueOf(i)).start();}for (int i = 1; i <= 3; i++) {final int num = i;new Thread(()->{myCache.get(num+"");},String.valueOf(i)).start();}} } //結(jié)果(還沒寫完就重新再寫一個(gè)) 1 正在寫1 3 正在寫3 2 正在寫2 1 正在讀1 2 正在讀2 3 正在讀3 2 寫完了2 3 讀完了null 2 讀完了null 1 讀完了null 1 寫完了1 3 寫完了3

完善代碼:

class MyCache{private volatile Map<String,String> map = new HashMap<>();private ReadWriteLock rwLock = new ReentrantReadWriteLock();public void put(String key,String value){rwLock.writeLock().lock();try {System.out.println(Thread.currentThread().getName()+"\t 準(zhǔn)備寫入數(shù)據(jù)"+key);TimeUnit.MILLISECONDS.sleep(200);map.put(key, value);System.out.println(Thread.currentThread().getName()+"\t 寫入數(shù)據(jù)完成"+key);} catch (Exception e) {e.printStackTrace();} finally {rwLock.writeLock().unlock();}}public void get(String key){rwLock.readLock().lock();try {System.out.println(Thread.currentThread().getName()+"\t 準(zhǔn)備讀取數(shù)據(jù)"+key);TimeUnit.MILLISECONDS.sleep(200);String value = map.get(key);System.out.println(Thread.currentThread().getName()+"\t 讀取數(shù)據(jù)完成"+value);} catch (Exception e) {e.printStackTrace();} finally {rwLock.readLock().unlock();}} } public class ReadWriteLockDemo {public static void main(String[] args) throws InterruptedException {MyCache myCache = new MyCache();for (int i = 1; i <=3 ; i++) {String key = String.valueOf(i);new Thread(()->{myCache.put(key,UUID.randomUUID().toString().substring(0,8));},String.valueOf(i)).start();}TimeUnit.SECONDS.sleep(2);for (int i = 1; i <=3 ; i++) {String key = String.valueOf(i);new Thread(()->{myCache.get(key);},String.valueOf(i)).start();}} } 2 準(zhǔn)備寫入數(shù)據(jù)2 2 寫入數(shù)據(jù)完成2 1 準(zhǔn)備寫入數(shù)據(jù)1 1 寫入數(shù)據(jù)完成1 3 準(zhǔn)備寫入數(shù)據(jù)3 3 寫入數(shù)據(jù)完成3 1 準(zhǔn)備讀取數(shù)據(jù)1 2 準(zhǔn)備讀取數(shù)據(jù)2 3 準(zhǔn)備讀取數(shù)據(jù)3 1 讀取數(shù)據(jù)完成c013a300 2 讀取數(shù)據(jù)完成347ce814 3 讀取數(shù)據(jù)完成7387e9c6

參考文章

總結(jié)

以上是生活随笔為你收集整理的Lock接口Condition,以及Lock与synchronized异同的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。