Java?中的synchronized?與?Lock對(duì)比及使用場(chǎng)景解析
?? 前言
在多線程并發(fā)編程中,線程安全問題始終是開發(fā)者需要重點(diǎn)關(guān)注的核心內(nèi)容之一。Java 提供了多種機(jī)制來實(shí)現(xiàn)同步控制,其中最常用的兩種方式是:
- 使用
synchronized關(guān)鍵字 - 使用
java.util.concurrent.locks.Lock接口(如ReentrantLock)
雖然兩者都能實(shí)現(xiàn)線程同步功能,但它們?cè)谑褂梅绞?、靈活性、可擴(kuò)展性以及性能優(yōu)化方面存在顯著差異。
本文將從底層原理、語(yǔ)法結(jié)構(gòu)、使用場(chǎng)景、優(yōu)缺點(diǎn)、最佳實(shí)踐等多個(gè)維度對(duì) synchronized 和 Lock 進(jìn)行全面深入的解析,并通過大量代碼示例幫助你更好地理解它們之間的區(qū)別與聯(lián)系。
?? 一、synchronized關(guān)鍵字詳解
1. 基本概念
synchronized 是 Java 內(nèi)置的關(guān)鍵字,用于保證多個(gè)線程對(duì)共享資源訪問時(shí)的互斥性和可見性。它可以修飾方法或代碼塊,確保同一時(shí)刻只有一個(gè)線程可以執(zhí)行被同步的代碼。
2. 使用方式
(1)修飾實(shí)例方法
public synchronized void method() {
// 同步整個(gè)方法體
}
此時(shí)鎖對(duì)象是當(dāng)前類的實(shí)例(即 this)。
(2)修飾靜態(tài)方法
public static synchronized void staticMethod() {
// 同步靜態(tài)方法
}
此時(shí)鎖對(duì)象是當(dāng)前類的 Class 對(duì)象(即 ClassName.class)。
(3)修飾代碼塊(推薦)
public void method() {
synchronized (this) {
// 同步代碼塊
}
}
更靈活,可以指定任意對(duì)象作為鎖,推薦使用這種方式以減少鎖定范圍。
3. 特性總結(jié)
| 特性 | 描述 |
|---|---|
| 自動(dòng)釋放鎖 | JVM 在同步塊執(zhí)行結(jié)束后自動(dòng)釋放鎖 |
| 不可中斷 | 等待獲取鎖的線程無(wú)法被中斷 |
| 非公平鎖 | 多個(gè)線程競(jìng)爭(zhēng)時(shí),不保證先等待的線程優(yōu)先獲得鎖 |
| 可重入性 | 支持同一個(gè)線程多次獲取同一把鎖 |
?? 二、Lock接口詳解(以ReentrantLock為例)
1. 基本概念
Lock 是 Java 5 引入的一個(gè)接口,位于 java.util.concurrent.locks 包下。常見的實(shí)現(xiàn)類有:
ReentrantLock:可重入鎖ReadWriteLock:讀寫分離鎖(實(shí)現(xiàn)類為ReentrantReadWriteLock)
相比 synchronized,Lock 更加靈活和強(qiáng)大,提供了更多高級(jí)功能。
2. 使用方式
Lock lock = new ReentrantLock();
lock.lock(); // 手動(dòng)加鎖
try {
// 臨界區(qū)邏輯
} finally {
lock.unlock(); // 必須放在 finally 塊中釋放鎖
}
?? 注意:必須手動(dòng)調(diào)用 unlock(),否則可能導(dǎo)致死鎖!
3. 核心特性
| 特性 | 描述 |
|---|---|
| 手動(dòng)管理鎖 | 需要顯式調(diào)用 lock() 和 unlock() |
| 可中斷等待 | 支持線程在等待鎖的過程中響應(yīng)中斷(lockInterruptibly()) |
| 超時(shí)獲取鎖 | 支持嘗試獲取鎖并設(shè)置超時(shí)時(shí)間(tryLock(long time, TimeUnit unit)) |
| 公平鎖/非公平鎖 | 構(gòu)造函數(shù)可選擇是否啟用公平鎖 |
| 條件變量支持 | 提供 Condition 接口,實(shí)現(xiàn)更細(xì)粒度的線程通信 |
?? 三、synchronized與Lock的核心區(qū)別對(duì)比表
| 功能 | synchronized | Lock |
|---|---|---|
| 加鎖方式 | 自動(dòng)加鎖、解鎖 | 手動(dòng)加鎖、解鎖 |
| 鎖類型 | 非公平鎖 | 可選公平/非公平 |
| 可中斷 | ? 不支持 | ? 支持 |
| 超時(shí)機(jī)制 | ? 不支持 | ? 支持 |
| 嘗試獲取鎖 | ? 不支持 | ? 支持 |
| 條件變量 | ? 不支持 | ? 支持 |
| 性能優(yōu)化 | JDK 1.6+ 已優(yōu)化 | 更適合高并發(fā)場(chǎng)景 |
| 適用場(chǎng)景 | 簡(jiǎn)單同步需求 | 復(fù)雜并發(fā)控制場(chǎng)景 |
?? 四、使用場(chǎng)景對(duì)比與建議
| 場(chǎng)景 | 推薦使用 | 說明 |
|---|---|---|
| 簡(jiǎn)單方法或代碼塊同步 | synchronized | 實(shí)現(xiàn)簡(jiǎn)單,無(wú)需手動(dòng)釋放鎖 |
| 高并發(fā)、復(fù)雜同步控制 | Lock | 提供更多控制選項(xiàng),如公平鎖、嘗試鎖等 |
| 需要線程中斷響應(yīng) | Lock | synchronized 不支持中斷等待 |
| 需要條件變量配合 | Lock | Condition 可替代傳統(tǒng)的 wait/notify |
| 需要超時(shí)獲取鎖 | Lock | tryLock() 方法非常實(shí)用 |
?? 五、實(shí)戰(zhàn)案例分析
案例 1:帶超時(shí)的鎖獲?。ㄟm用于防止死鎖)
Lock lock = new ReentrantLock();
boolean isLocked = false;
try {
isLocked = lock.tryLock(3, TimeUnit.SECONDS);
if (isLocked) {
try {
// 執(zhí)行業(yè)務(wù)邏輯
} finally {
lock.unlock();
}
} else {
System.out.println("未能在3秒內(nèi)獲取到鎖");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("線程被中斷");
}案例 2:使用Condition實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模型
class BoundedQueue {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final Queue<Integer> queue = new LinkedList<>();
private final int capacity;
public BoundedQueue(int capacity) {
this.capacity = capacity;
}
public void put(int value) throws InterruptedException {
lock.lock();
try {
while (queue.size() == capacity) {
notFull.await(); // 等待隊(duì)列不滿
}
queue.add(value);
notEmpty.signal(); // 喚醒消費(fèi)者
} finally {
lock.unlock();
}
}
public int take() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await(); // 等待隊(duì)列不空
}
return queue.poll();
} finally {
lock.unlock();
}
}
}?? 六、底層原理簡(jiǎn)析(進(jìn)階)
1.synchronized的底層實(shí)現(xiàn)
在 JVM 層面,synchronized 是基于 Monitor(監(jiān)視器)機(jī)制實(shí)現(xiàn)的。每個(gè) Java 對(duì)象都關(guān)聯(lián)一個(gè) Monitor,當(dāng)線程進(jìn)入同步塊時(shí),會(huì)嘗試獲取該對(duì)象的 Monitor,成功則進(jìn)入,失敗則阻塞。
JVM 對(duì)其進(jìn)行了多項(xiàng)優(yōu)化,包括:
- 偏向鎖(Biased Locking)
- 輕量級(jí)鎖(Lightweight Locking)
- 自旋鎖(Spin Lock)
- 鎖粗化(Lock Coarsening)
- 鎖消除(Lock Elimination)
這些優(yōu)化使得 synchronized 在現(xiàn)代 JVM 上表現(xiàn)優(yōu)異。
2.ReentrantLock的底層實(shí)現(xiàn)
ReentrantLock 底層依賴于 AbstractQueuedSynchronizer(AQS)框架,是一個(gè)基于 CLH(Craig, Landin, and Hagersten)隊(duì)列的同步工具。
它通過 CAS(Compare and Swap)操作和 volatile 變量實(shí)現(xiàn)線程安全,具有更高的可控性和靈活性。
??? 七、最佳實(shí)踐與注意事項(xiàng)
| 建議 | 說明 |
|---|---|
優(yōu)先考慮 synchronized | 如果只是簡(jiǎn)單的同步,優(yōu)先使用 synchronized,避免復(fù)雜代碼 |
Lock 放在 finally 中釋放 | 防止因異常導(dǎo)致死鎖 |
使用 tryLock() 防止死鎖 | 在某些情況下,嘗試獲取鎖比無(wú)限等待更合理 |
| 避免嵌套鎖 | 容易引發(fā)死鎖,應(yīng)盡量避免或使用工具檢測(cè) |
| 選擇公平鎖需謹(jǐn)慎 | 公平鎖雖然保證順序,但可能帶來性能損耗 |
使用 Condition 替代 wait/notify | 更清晰、線程安全 |
?? 八、總結(jié)
| 項(xiàng)目 | synchronized | Lock |
|---|---|---|
| 是否內(nèi)置 | ? 是 | ? 否 |
| 使用難度 | 簡(jiǎn)單 | 復(fù)雜 |
| 控制粒度 | 粗 | 細(xì) |
| 功能豐富度 | 一般 | 強(qiáng)大 |
| 性能表現(xiàn) | 好 | 更好(高并發(fā)) |
| 推薦用途 | 初學(xué)者、簡(jiǎn)單同步 | 高級(jí)用戶、復(fù)雜并發(fā)控制 |
在實(shí)際開發(fā)中,兩者各有優(yōu)勢(shì),選擇哪一個(gè)取決于具體的應(yīng)用場(chǎng)景和團(tuán)隊(duì)技術(shù)棧。對(duì)于大多數(shù)中小型項(xiàng)目,synchronized 已經(jīng)足夠;而在需要更高并發(fā)控制能力的場(chǎng)景下,Lock 更具優(yōu)勢(shì)。
到此這篇關(guān)于Java 中的 synchronized 與 Lock:深度對(duì)比、使用場(chǎng)景及高級(jí)用法的文章就介紹到這了,更多相關(guān)Java synchronized 與 Lock內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- java線程中synchronized和Lock區(qū)別及介紹
- 一文帶你搞懂Java中Synchronized和Lock的原理與使用
- Java常用鎖synchronized和ReentrantLock的區(qū)別
- 淺談Java中Lock和Synchronized的區(qū)別
- java中synchronized Lock(本地同步)鎖的8種情況
- 詳解Java中的鎖Lock和synchronized
- 深入理解java內(nèi)置鎖(synchronized)和顯式鎖(ReentrantLock)
- 深入Synchronized和java.util.concurrent.locks.Lock的區(qū)別詳解
- Java編程synchronized與lock的區(qū)別【推薦】
相關(guān)文章
alibaba?seata服務(wù)端具體實(shí)現(xiàn)
seata是來處理分布式服務(wù)之間互相調(diào)用的事務(wù)問題,本文重點(diǎn)給大家介紹alibaba-seata實(shí)現(xiàn)方法,文中通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02
Java 8 Optional.ofNullable() 方法如何優(yōu)雅處理 nu
Optional.ofNullable()是Java8用于安全包裝可能為null值的工具,避免空指針異常,相比Optional.of(),它能容忍null并返回空對(duì)象,本文給大家介紹Java8 Optional.ofNullable()方法如何優(yōu)雅處理null值,感興趣的朋友一起看看吧2025-09-09
Java中的線程池ThreadPoolExecutor深入解析
這篇文章主要介紹了Java中的線程池ThreadPoolExecutor深入解析,線程池,thread pool,是一種線程使用模式,線程池維護(hù)著多個(gè)線程,等待著監(jiān)督管理者分配可并發(fā)執(zhí)行的任務(wù),需要的朋友可以參考下2023-11-11
gateway和jwt網(wǎng)關(guān)認(rèn)證實(shí)現(xiàn)過程解析
這篇文章主要介紹了gateway和jwt網(wǎng)關(guān)認(rèn)證實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
Spring學(xué)習(xí)通過AspectJ注解方式實(shí)現(xiàn)AOP操作
這篇文章主要為大家介紹了Spring學(xué)習(xí)通過AspectJ注解方式實(shí)現(xiàn)AOP操作,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
javaWeb實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了javaWeb實(shí)現(xiàn)學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
Spring?Boot?整合持久層之Spring Data JPA
在介紹Spring Data JPA的時(shí)候,我們首先認(rèn)識(shí)下Hibernate。Hibernate是數(shù)據(jù)訪問解決技術(shù)的絕對(duì)霸主,使用O/R映射技術(shù)實(shí)現(xiàn)數(shù)據(jù)訪問,O/R映射即將領(lǐng)域模型類和數(shù)據(jù)庫(kù)的表進(jìn)行映射,通過程序操作對(duì)象而實(shí)現(xiàn)表數(shù)據(jù)操作的能力,讓數(shù)據(jù)訪問操作無(wú)須關(guān)注數(shù)據(jù)庫(kù)相關(guān)的技術(shù)2022-08-08
JavaCV實(shí)現(xiàn)多個(gè)MP4視頻的合并
這篇文章主要為大家詳細(xì)介紹了如何使用javacv和ffmpeg框架實(shí)現(xiàn)簡(jiǎn)單快速的合并mp4文件的視頻和音頻,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-10-10

