欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java并發(fā)編程之ReentrantLock可重入鎖的實(shí)例代碼

 更新時(shí)間:2021年02月06日 14:21:04   作者:Java硬件工程師  
這篇文章主要介紹了Java并發(fā)編程之ReentrantLock可重入鎖的實(shí)例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

目錄 1.ReentrantLock可重入鎖概述2.可重入3.可打斷4.鎖超時(shí)5.公平鎖6.條件變量 Condition

1.ReentrantLock可重入鎖概述

相對(duì)于 synchronized 它具備如下特點(diǎn)
可中斷
synchronized鎖加上去不能中斷,a線程應(yīng)用鎖,b線程不能取消掉它
可以設(shè)置超時(shí)時(shí)間
synchronized它去獲取鎖時(shí),如果對(duì)方持有鎖,那么它就會(huì)進(jìn)入entryList一直等待下去。而可重入鎖可以設(shè)置超時(shí)時(shí)間,規(guī)定時(shí)間內(nèi)如果獲取不到鎖,就放棄鎖
可以設(shè)置為公平鎖
防止線程饑餓的情況,即先到先得。如果爭(zhēng)搶的人比較多,則可能會(huì)發(fā)生永遠(yuǎn)都得不到鎖

支持多個(gè)條件變量多個(gè)waitset(不支持條件一的去a不支持條件二的去b)
synchronized只支持同一個(gè)waitset.
與 synchronized 一樣,都支持可重入

基本語法

// 獲取鎖
reentrantLock.lock();
try {
 // 臨界區(qū)
} finally {
 // 釋放鎖
 reentrantLock.unlock();
}

synchronized是在關(guān)鍵字的級(jí)別來保護(hù)臨界區(qū),而reentrantLock是在對(duì)象的級(jí)別保護(hù)臨界區(qū)。臨界區(qū)即訪問共享資源的那段代碼。finally中表明不管將來是否出現(xiàn)異常,都會(huì)釋放鎖,釋放鎖即調(diào)用unlock方法。否則無法釋放鎖,其它線程就永遠(yuǎn)也獲取不了鎖。

2.可重入

可重入是指同一個(gè)線程如果首次獲得了這把鎖,那么因?yàn)樗沁@把鎖的擁有者,因此有權(quán)利再次獲取這把鎖
如果是不可重入鎖,那么第二次獲得鎖時(shí),自己也會(huì)被鎖擋住
ReentrantLock和synchronized都是可重入鎖。

public class TestReentranLock1 {
 static ReentrantLock lock = new ReentrantLock();
 public static void main(String[] args) {
  method1();
 }
 public static void method1() {
  lock.lock();
  try {
   System.out.println("execute method1");
   method2();
  } finally {
   lock.unlock();
  }
 }
 public static void method2() {
  lock.lock();
  try {
   System.out.println("execute method2");
   method3();
  } finally {
   lock.unlock();
  }
 }
 public static void method3() {
  lock.lock();
  try {
   System.out.println("execute method3");
  } finally {
   lock.unlock();
  }
 }
}
execute method1
execute method2
execute method3

3.可打斷

可打斷是指在等待鎖的過程中,其它線程可以用interrupt方法終止我的等待。synchronized鎖是不可打斷的。
我們要想在等鎖的過程中被打斷,就要使用lockInterruptibly()方法對(duì)lock對(duì)象加鎖,而不是lock()方法

public class TestReentranLock2 {
 public static void main(String[] args) {
  ReentrantLock lock = new ReentrantLock();
  Thread t1 = new Thread(() -> {
   try {
    //如果沒有競(jìng)爭(zhēng),此方法就會(huì)獲取lock對(duì)象的鎖
    //如果有競(jìng)爭(zhēng),就進(jìn)入阻塞隊(duì)列等待,可以被其它線程用interrupt打斷
    System.out.println("嘗試獲得鎖");
    lock.lockInterruptibly();
   } catch (InterruptedException e) {
    e.printStackTrace();
    System.out.println("等鎖的過程中被打斷");
    return;
   }
   try {
    System.out.println("t1獲得了鎖");
   } finally {
    lock.unlock();
   }
  }, "t1");
  lock.lock();
  System.out.println("主線程獲得了鎖");
  t1.start();
  try {
   try {
    sleep(1);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   t1.interrupt();
   System.out.println("執(zhí)行打斷t1");
  } finally {
   lock.unlock();
  }
 }
}
主線程獲得了鎖
嘗試獲得鎖
執(zhí)行打斷t1
等鎖的過程中被打斷
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 cn.yj.jvm.TestReentranLock2.lambda$main$0(TestReentranLock2.java:15)
	at java.lang.Thread.run(Thread.java:748)

注意如果是不可中斷模式,那么即使使用了 interrupt 也不會(huì)讓等待中斷,即不是。即使用lock()方法。
這種方式可以避免死鎖情況的發(fā)生,避免無休止的等待。

ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
 System.out.println("啟動(dòng)...");
 lock.lock();
 try {
  System.out.println("獲得了鎖");
 } finally {
  lock.unlock();
 }
}, "t1");
lock.lock();
System.out.println("獲得了鎖");
t1.start();
try {
 sleep(1);
 t1.interrupt();
 System.out.println("執(zhí)行打斷");
 sleep(1);
} finally {
 System.out.println("釋放了鎖");
 lock.unlock();
}

4.鎖超時(shí)

ReentranLock支持可打斷,其實(shí)就是為了避免死等,這樣就可以減少死鎖的發(fā)生。實(shí)際上可打斷這種方式屬于一種被動(dòng)的避免死等,是由其它線程interrupt來打斷。
而鎖超時(shí)是主動(dòng)的方式避免死等的手段。
獲取鎖用tryLock()方法,即嘗試獲得鎖,如果成功了,它就獲得鎖,如果失敗了,它就可以不去進(jìn)入阻塞隊(duì)列等待,它就會(huì)返回false,表示沒有獲得鎖

立刻失敗

public static void main(String[] args) {
  ReentrantLock lock = new ReentrantLock();
  Thread t1 = new Thread(() -> {
   System.out.println("啟動(dòng)...");
   if (!lock.tryLock()) {
    System.out.println("獲取不到鎖,立刻失敗,返回");
    return;
   }
   try {
    System.out.println("獲得了鎖");
   } finally {
    lock.unlock();
   }
  }, "t1");
  lock.lock();
  System.out.println("獲得了鎖");
  t1.start();
  try {
   try {
    sleep(500);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  } finally {
   lock.unlock();
  }
}

獲得了鎖
啟動(dòng)...
獲取不到鎖,立刻失敗,返回

超時(shí)失敗
lock.tryLock(1,TimeUnit.SECONDS)表示嘗試等待1s,如果主線程不釋放鎖,那么它就會(huì)返回false,如果釋放了鎖,那么它就會(huì)返回true.tryLock也支持被打斷,被打斷時(shí)報(bào)異常。

ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
 log.debug("啟動(dòng)...");
 try {
  if (!lock.tryLock(1, TimeUnit.SECONDS)) {
   log.debug("獲取等待 1s 后失敗,返回");
   return;
  }
 } catch (InterruptedException e) {
  e.printStackTrace();
 }
 try {
  log.debug("獲得了鎖");
 } finally {
  lock.unlock();
 }
}, "t1");
lock.lock();
log.debug("獲得了鎖");
t1.start();
try {
 sleep(2);
} finally {
 lock.unlock();
}

輸出

18:19:40.537 [main] c.TestTimeout - 獲得了鎖
18:19:40.544 [t1] c.TestTimeout - 啟動(dòng)...
18:19:41.547 [t1] c.TestTimeout - 獲取等待 1s 后失敗,返回

5.公平鎖

對(duì)于synchronized來說,它是不公平的鎖。當(dāng)一個(gè)線程持有鎖,其他線程就會(huì)進(jìn)入阻塞隊(duì)列等待,當(dāng)鎖的持有者釋放鎖的時(shí)候,這些線程就會(huì)一擁而上,誰先搶到,誰就成為monitor的主人,而不會(huì)按照先來先得的規(guī)則。

ReentrantLock 默認(rèn)是不公平的
ReentrantLock有一個(gè)帶參構(gòu)造方法。默認(rèn)是非公平的。

 public ReentrantLock(boolean fair) {
  sync = fair ? new FairSync() : new NonfairSync();
}

 

我們可以通過布爾值改成真,來保證它的公平性。即將來阻塞隊(duì)列里的線程,爭(zhēng)搶鎖的時(shí)候會(huì)按照進(jìn)入阻塞隊(duì)列的順序執(zhí)行,先到先得。

6.條件變量 Condition

synchronized 中也有條件變量,就是我們講原理時(shí)那個(gè) waitSet 休息室,當(dāng)條件不滿足時(shí)進(jìn)入 waitSet 等待

ReentrantLock 的條件變量比 synchronized 強(qiáng)大之處在于,它是支持多個(gè)條件變量的,這就好比

synchronized 是那些不滿足條件的線程都在一間休息室等消息
而 ReentrantLock 支持多間休息室,有專門等煙的休息室、專門等早餐的休息室、喚醒時(shí)也是按休息室來喚醒

使用要點(diǎn):

  • await 前需要獲得鎖
  • await 執(zhí)行后,會(huì)釋放鎖,進(jìn)入 conditionObject 等待
  • await 的線程被喚醒(或打斷、或超時(shí))取重新競(jìng)爭(zhēng) lock 鎖
  • 競(jìng)爭(zhēng) lock 鎖成功后,從 await 后繼續(xù)執(zhí)行
  • signal 相當(dāng)于 notify,signalAll 相當(dāng)于 notifyAll
static ReentrantLock lock = new ReentrantLock();
static Condition waitCigaretteQueue = lock.newCondition();
static Condition waitbreakfastQueue = lock.newCondition();
static volatile boolean hasCigrette = false;
static volatile boolean hasBreakfast = false;
public static void main(String[] args) {
 new Thread(() -> {
  try {
   lock.lock();
   while (!hasCigrette) {
    try {
     waitCigaretteQueue.await();
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
   log.debug("等到了它的煙");
  } finally {
   lock.unlock();
  }
 }).start();
 new Thread(() -> {
  try {
   lock.lock();
   while (!hasBreakfast) {
    try {
     waitbreakfastQueue.await();
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
   log.debug("等到了它的早餐");
  } finally {
   lock.unlock();
  }
 }).start();
 sleep(1);
 sendBreakfast();
 sleep(1);
 sendCigarette();
}
private static void sendCigarette() {
 lock.lock();
 try {
  log.debug("送煙來了");
  hasCigrette = true;
  waitCigaretteQueue.signal();
 } finally {
  lock.unlock();
 }
}
private static void sendBreakfast() {
 lock.lock();
 try {
  log.debug("送早餐來了");
  hasBreakfast = true;
  waitbreakfastQueue.signal();
 } finally {
  lock.unlock();
 }
}

輸出

18:52:27.680 [main] c.TestCondition - 送早餐來了
18:52:27.682 [Thread-1] c.TestCondition - 等到了它的早餐
18:52:28.683 [main] c.TestCondition - 送煙來了
18:52:28.683 [Thread-0] c.TestCondition - 等到了它的煙

到此這篇關(guān)于Java并發(fā)編程之ReentrantLock可重入鎖的實(shí)例代碼的文章就介紹到這了,更多相關(guān)Java ReentrantLock可重入鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java FastJson的簡(jiǎn)單用法

    java FastJson的簡(jiǎn)單用法

    FastJson是阿里的開源JSON解析庫,可以解析JSON格式的字符串,支持將Java Bean序列化為JSON字符串,也可以從JSON字符串反序列化到JavaBean,這里我介紹一下FastJson的使用,感興趣的朋友一起看看吧
    2021-09-09
  • SpringCloud引入feign失敗或找不到@EnableFeignClients注解問題

    SpringCloud引入feign失敗或找不到@EnableFeignClients注解問題

    這篇文章主要介紹了SpringCloud引入feign失敗或找不到@EnableFeignClients注解問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • java Struts2框架下實(shí)現(xiàn)文件上傳功能

    java Struts2框架下實(shí)現(xiàn)文件上傳功能

    這篇文章主要為大家詳細(xì)介紹了java Struts2框架下實(shí)現(xiàn)文件上傳功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • SpringBoot啟動(dòng)并初始化執(zhí)行sql腳本問題

    SpringBoot啟動(dòng)并初始化執(zhí)行sql腳本問題

    這篇文章主要介紹了SpringBoot啟動(dòng)并初始化執(zhí)行sql腳本問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • SpringBoot通過自定義注解實(shí)現(xiàn)參數(shù)校驗(yàn)

    SpringBoot通過自定義注解實(shí)現(xiàn)參數(shù)校驗(yàn)

    實(shí)現(xiàn)參數(shù)校驗(yàn)說實(shí)話方式還挺多,個(gè)人使用過直接在Controller代碼里面寫、AOP+自定義注解、ConstraintValidator。本文主要和大家講的是ConstraintValidator實(shí)現(xiàn),感興趣的可以了解一下
    2022-12-12
  • MyBatis 中 SqlMapConfig 配置文件詳解

    MyBatis 中 SqlMapConfig 配置文件詳解

    這篇文章主要介紹了MyBatis 中 SqlMapConfig 配置文件詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • 詳細(xì)解讀Java編程中面向字符的輸入流

    詳細(xì)解讀Java編程中面向字符的輸入流

    這篇文章主要介紹了Java中面向字符的輸入和輸出流,是Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-10-10
  • java常見的序列化方式

    java常見的序列化方式

    這篇文章主要為大家詳細(xì)介紹了java中兩種常見的序列化方式,感興趣的小伙伴們可以參考一下
    2016-08-08
  • Maven實(shí)現(xiàn)自己的starter依賴

    Maven實(shí)現(xiàn)自己的starter依賴

    本文主要介紹了Maven實(shí)現(xiàn)自己的starter依賴,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • SpringBoot3集成WebSocket的全過程

    SpringBoot3集成WebSocket的全過程

    WebSocket通過一個(gè)TCP連接在客戶端和服務(wù)器之間建立一個(gè)全雙工、雙向的通信通道,使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單,本文給大家介紹了SpringBoot3集成WebSocket的全過程,并有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下
    2024-05-05

最新評(píng)論