Java中JUC包(java.util.concurrent)下的常用子類
一、對(duì)象鎖juc.locks包
在Java中除了synchronized關(guān)鍵字可以實(shí)現(xiàn)對(duì)象鎖之外,java.util.concurrent中的Lock接口也可以實(shí)現(xiàn)對(duì)象鎖。
介紹一下這個(gè)lock鎖的簡(jiǎn)要實(shí)現(xiàn):
- JDK1.0就有的,需要JVM借助操作系統(tǒng)提供的mutex系統(tǒng)原語實(shí)現(xiàn)
- JDK1.5之后,Java語言自己實(shí)現(xiàn)的互斥鎖實(shí)現(xiàn),不需要借助操作系統(tǒng)的monitor機(jī)制。
注:使用Lock接口需要顯式的進(jìn)行加鎖和解鎖操作。
我們可以使用Lock接口的實(shí)現(xiàn)子類ReentrantLock來進(jìn)行加鎖解鎖:
ReentrantLock 可重入互斥鎖. 和 synchronized 定位類似, 都是用來實(shí)現(xiàn)互斥效果, 保證線程安全.
ReentrantLock 的用法:
lock()
: 加鎖,獲取鎖失敗的線程進(jìn)入阻塞狀態(tài),直到其他線程釋放鎖,再次競(jìng)爭(zhēng),死等。trylock
(超時(shí)時(shí)間): 加鎖, 獲取鎖失敗的線程進(jìn)入阻塞態(tài),等待一段時(shí)間,時(shí)間過了若還未獲取到鎖恢復(fù)執(zhí)行,放棄加鎖,執(zhí)行其他代碼unlock()
: 解鎖
synchronized和lock的區(qū)別:
synchronized
是Java的關(guān)鍵字, 由 JVM 實(shí)現(xiàn),需要依賴操作系統(tǒng)提供的線程互斥原語(mutex),而Lock
標(biāo)準(zhǔn)庫的類和接口,其中一個(gè)最常用的子類( ReentrantLock ,可重入鎖),由Java本身實(shí)現(xiàn)的,不需要依賴操作系統(tǒng)。
synchronized
隱式的加鎖和解鎖,lock
需要顯示進(jìn)行加鎖和解鎖
synchronized
在獲取鎖失敗的線程時(shí),死等;lock
可以使用trylock
等待一段時(shí)間之后自動(dòng)放棄加鎖,線程恢復(fù)執(zhí)行
synchronized
是非公平鎖, ReentrantLock
默認(rèn)是非公平鎖. 可以通過構(gòu)造方法傳入一個(gè) true
開啟公平鎖模式.
synchronized
不支持讀寫鎖,Lock
子類ReentrantReadWriteLock
支持讀寫鎖。
更強(qiáng)大的喚醒機(jī)制. synchronized
是通過 Object 的 wait / notify
實(shí)現(xiàn)等待-喚醒. 每次喚醒的是一個(gè)隨機(jī)等待的線程.ReentrantLock
搭配 Condition
類實(shí)現(xiàn)等待-喚醒, 可以更精確控制喚醒某個(gè)指定的線程
小結(jié):
一般場(chǎng)景synchronized足夠用了,需要用超時(shí)等待鎖,公平鎖,讀寫鎖再考慮使用juc.lock
如何選擇使用哪個(gè)鎖?
- 鎖競(jìng)爭(zhēng)不激烈的時(shí)候, 使用 synchronized, 效率更高, 自動(dòng)釋放更方便.
- 鎖競(jìng)爭(zhēng)激烈的時(shí)候, 使用 ReentrantLock, 搭配 trylock 更靈活控制加鎖的行為, 而不是死等.
- 如果需要使用公平鎖, 使用 ReentrantLock.
二、原子類
原子類內(nèi)部用的是 CAS 實(shí)現(xiàn),所以性能要比加鎖實(shí)現(xiàn) i++ 高很多。原子類有以下幾個(gè):
- AtomicBoolean
- AtomicInteger
- AtomicIntegerArray
- AtomicLong
- AtomicReference
- AtomicStampedReference
以 AtomicInteger 舉例,常見方法有:
addAndGet(int delta); i += delta; decrementAndGet(); --i; getAndDecrement(); i--; incrementAndGet(); ++i; getAndIncrement(); i++;
三、四個(gè)常用工具類
juc包下一共有四個(gè)常用工具類:
- 信號(hào)量 - Semaphore
- 計(jì)數(shù)器 - CountDownLatch
- 循環(huán)柵欄 - CyclicBarrier
- 兩個(gè)線程之間的交換器 - Exchanger
3.1 信號(hào)量 Semaphore
信號(hào)量Semaphore就是一個(gè)計(jì)數(shù)器,表示當(dāng)前可用資源的個(gè)數(shù)
關(guān)于信號(hào)量Semaphore有兩個(gè)核心操作:
- P - 申請(qǐng)資源操作
- V - 釋放資源操作
Semaphore 的PV加減操作都是原子性的,再多線程場(chǎng)景下可以直接使用
public static void main(String[] args) { // 在構(gòu)造參數(shù)傳入可用資源的個(gè)數(shù) // 可用資源為6個(gè) Semaphore semaphore = new Semaphore(6); Runnable runnable = new Runnable() { @Override public void run() { try { System.out.println(Thread.currentThread().getName() + "準(zhǔn)備申請(qǐng)資源"); // P操作,每次申請(qǐng)兩個(gè)資源 semaphore.acquire(2); System.out.println(Thread.currentThread().getName() + "獲取資源成功"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "釋放資源"); // V操作,默認(rèn)釋放一個(gè)占有的資源 semaphore.release(2); }catch (InterruptedException e) { e.printStackTrace(); } } }; for (int i = 0; i < 20; i++) { Thread t = new Thread(runnable,String.valueOf(i + 1)); t.start(); } }
3.2 CountDownLatch
有點(diǎn)類似于大號(hào)的join方法
調(diào)用await方法的線程需要等待其他線程將計(jì)數(shù)器減為0才能繼續(xù)恢復(fù)執(zhí)行。
public static void main(String[] args) throws InterruptedException { // 等待線程需要等待的線程數(shù),必須等這10個(gè)子線程全部執(zhí)行完畢再恢復(fù)執(zhí)行 CountDownLatch latch = new CountDownLatch(10); Runnable runnable = new Runnable() { @Override public void run() { try { Thread.sleep(new Random().nextInt(1000)); System.out.println(Thread.currentThread().getName() + "到達(dá)終點(diǎn)"); // 計(jì)數(shù)器 - 1 latch.countDown(); }catch (InterruptedException e) { e.printStackTrace(); } } }; for (int i = 0; i < 10; i++) { Thread t = new Thread(runnable,"運(yùn)動(dòng)員" + i + 1); t.start(); } // main線程就是裁判線程,需要等待所有運(yùn)動(dòng)員到底終點(diǎn)再恢復(fù)執(zhí)行 // 直到所有線程調(diào)用countdown方法將計(jì)數(shù)器減為0繼續(xù)執(zhí)行 latch.await(); System.out.println("比賽結(jié)束~最終獲勝的是鵬哥,有請(qǐng)冠軍給大家高歌一首~"); }
總結(jié)
至于CyclicBarrier和Exchanger在本篇就不多介紹,讀者可以自行查閱一下官方文檔進(jìn)行仔細(xì)的學(xué)習(xí)~如果有問題可以私信博主,別忘了點(diǎn)贊收藏+關(guān)注哦!
相關(guān)文章
Oracle + Mybatis實(shí)現(xiàn)批量插入、更新和刪除示例代碼
利用MyBatis動(dòng)態(tài)SQL的特性,我們可以做一些批量的操作,下面這篇文章主要給大家介紹了關(guān)于Oracle + Mybatis實(shí)現(xiàn)批量插入、更新和刪除的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。2018-01-01Java+OpenCV實(shí)現(xiàn)人臉檢測(cè)并自動(dòng)拍照
這篇文章主要為大家詳細(xì)介紹了Java+OpenCV實(shí)現(xiàn)人臉檢測(cè),并調(diào)用筆記本攝像頭實(shí)時(shí)抓拍,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07Java實(shí)現(xiàn) 基于密度的局部離群點(diǎn)檢測(cè)------lof算法
這篇文章主要介紹了Java實(shí)現(xiàn) 基于密度的局部離群點(diǎn)檢測(cè)------lof算法,本文通過算法概述,算法Java源碼,測(cè)試結(jié)果等方面一一進(jìn)行說明,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07Java HashMap三種循環(huán)遍歷方式及其性能對(duì)比實(shí)例分析
這篇文章主要介紹了Java HashMap三種循環(huán)遍歷方式及其性能對(duì)比,結(jié)合具體實(shí)例形式分析了Java HashMap三種循環(huán)遍歷方式的實(shí)現(xiàn)方法、運(yùn)行效率及性能優(yōu)劣,需要的朋友可以參考下2019-10-10java求最大公約數(shù)與最小公倍數(shù)的方法示例
這篇文章主要介紹了java求最大公約數(shù)與最小公倍數(shù)的方法,涉及java數(shù)值運(yùn)算的相關(guān)操作技巧,并附帶分析了eclipse環(huán)境下設(shè)置運(yùn)行輸入?yún)?shù)的相關(guān)操作技巧,需要的朋友可以參考下2017-11-11springboot2中使用@JsonFormat注解不生效的解決
這篇文章主要介紹了springboot2中使用@JsonFormat注解不生效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02Java 字符串反轉(zhuǎn)實(shí)現(xiàn)代碼
這篇文章主要介紹了 Java 字符串反轉(zhuǎn)實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-03-03