Java中的分布式鎖與同步鎖使用詳解
什么是分布式鎖
分布式鎖是一種在分布式系統(tǒng)中用于協(xié)調(diào)多個(gè)節(jié)點(diǎn)訪問共享資源的機(jī)制。在分布式系統(tǒng)中,由于存在多個(gè)節(jié)點(diǎn)并行執(zhí)行任務(wù),可能會(huì)出現(xiàn)競(jìng)爭(zhēng)條件和數(shù)據(jù)不一致的問題。分布式鎖通過約束同一時(shí)刻只有一個(gè)節(jié)點(diǎn)能夠獲得鎖的方式,確保了對(duì)共享資源的獨(dú)占訪問,從而解決了這些問題。
分布式鎖的實(shí)現(xiàn)通常需要滿足以下特性:
- 互斥性:同一時(shí)刻只有一個(gè)節(jié)點(diǎn)能夠持有鎖,并且其他節(jié)點(diǎn)無法獲取該鎖。
- 可重入性:允許同一節(jié)點(diǎn)多次獲取同一個(gè)鎖,而不會(huì)發(fā)生死鎖。
- 容錯(cuò)性:在鎖持有者節(jié)點(diǎn)故障或網(wǎng)絡(luò)異常情況下,能夠及時(shí)釋放鎖。
- 超時(shí)處理:支持設(shè)置獲取鎖的最大等待時(shí)間,避免由于死鎖或長時(shí)間阻塞導(dǎo)致系統(tǒng)性能下降。
- 高可用性:具備高可用性能,即使部分節(jié)點(diǎn)故障也能正常提供服務(wù)。
實(shí)現(xiàn)分布式鎖的方法有多種,常見的包括:
- 基于數(shù)據(jù)庫的分布式鎖:利用數(shù)據(jù)庫的事務(wù)和唯一索引等特性,使用數(shù)據(jù)庫表記錄鎖的狀態(tài)。通過獲取和釋放特定的行鎖來實(shí)現(xiàn)互斥訪問。
- 基于緩存的分布式鎖:利用分布式緩存(如Redis)的原子性操作和過期時(shí)間等特點(diǎn),通過設(shè)置一個(gè)全局唯一的鍵值對(duì)表示鎖的狀態(tài)。只有成功獲取到鎖的節(jié)點(diǎn)才能在緩存中設(shè)置這個(gè)鍵值對(duì),其他節(jié)點(diǎn)則無法設(shè)置并獲取到鎖。
什么是同步鎖
同步鎖是一種并發(fā)控制機(jī)制,用于在多線程環(huán)境下保護(hù)共享資源的訪問。它可以防止多個(gè)線程同時(shí)訪問臨界區(qū)代碼,從而避免并發(fā)訪問導(dǎo)致的數(shù)據(jù)不一致或沖突。
同步鎖的原理是通過獲取鎖來獲得對(duì)臨界區(qū)代碼的獨(dú)占訪問權(quán)。在Java中,常用的同步鎖機(jī)制包括內(nèi)置鎖(也稱為監(jiān)視器鎖)和顯式鎖。
- 內(nèi)置鎖(Intrinsic Lock):也稱為對(duì)象監(jiān)視器鎖或synchronized鎖。當(dāng)一個(gè)方法或代碼塊被
synchronized
修飾時(shí),該對(duì)象上就存在一個(gè)內(nèi)置鎖。只有獲得了該鎖的線程才能執(zhí)行被修飾的方法或代碼塊,其他線程必須等待鎖的釋放。
示例代碼:
public class Example { private Object lock = new Object(); public void synchronizedMethod() { synchronized (lock) { // 臨界區(qū)代碼 } } }
- 顯式鎖(Explicit Lock):使用
java.util.concurrent.locks
包中的Lock接口及其實(shí)現(xiàn)類,如ReentrantLock。相比內(nèi)置鎖,顯式鎖提供了更靈活的鎖定機(jī)制,如可重入性、公平性和條件變量等特性,使得多線程代碼的控制更加精確。
示例代碼:
public class Example { private Lock lock = new ReentrantLock(); public void lockedMethod() { lock.lock(); try { // 臨界區(qū)代碼 } finally { lock.unlock(); } } }
同步鎖的使用可以有效避免多個(gè)線程對(duì)共享資源的不安全訪問,保證數(shù)據(jù)一致性和并發(fā)執(zhí)行的正確性。但過度使用同步鎖可能導(dǎo)致線程競(jìng)爭(zhēng)和性能下降,因此在設(shè)計(jì)多線程應(yīng)用程序時(shí)需要合理地使用同步鎖機(jī)制。
兩種鎖的使用場(chǎng)景
分布式鎖和同步鎖是在不同環(huán)境下用于實(shí)現(xiàn)并發(fā)控制的兩種機(jī)制。
- 分布式鎖的使用場(chǎng)景: 分布式鎖通常用于分布式系統(tǒng)中,用于協(xié)調(diào)多個(gè)節(jié)點(diǎn)對(duì)共享資源的訪問。
一個(gè)常見的場(chǎng)景是在分布式環(huán)境下實(shí)現(xiàn)對(duì)某個(gè)唯一資源的排他性訪問,例如分布式任務(wù)調(diào)度、分布式緩存更新等。
示例代碼(基于Redis實(shí)現(xiàn)分布式鎖):
import redis.clients.jedis.Jedis; public class DistributedLock { private Jedis jedis; private String lockKey; public boolean acquireLock() { long result = jedis.setnx(lockKey, "locked"); return result == 1; } public void releaseLock() { jedis.del(lockKey); } }
在上述代碼中,通過調(diào)用 setnx()
方法嘗試獲取鎖,如果返回值為1,則表示成功獲取到分布式鎖。釋放鎖的操作則是調(diào)用 del()
方法刪除鎖的鍵。
- 同步鎖的使用場(chǎng)景: 同步鎖主要用于多線程編程中,控制多個(gè)線程對(duì)共享資源的并發(fā)訪問。
一個(gè)經(jīng)典的場(chǎng)景是在多線程環(huán)境下保護(hù)對(duì)某個(gè)臨界區(qū)代碼的獨(dú)占訪問,避免數(shù)據(jù)競(jìng)爭(zhēng)和沖突。
示例代碼(基于Java內(nèi)置鎖實(shí)現(xiàn)同步鎖):
public class SynchronizedCounter { private int count; public synchronized void increment() { count++; } public synchronized void decrement() { count--; } }
在上述代碼中,通過 synchronized
關(guān)鍵字修飾方法,使得多線程在執(zhí)行這些方法時(shí)會(huì)自動(dòng)獲取對(duì)象的內(nèi)置鎖。
這樣可以確保同一時(shí)間只能有一個(gè)線程執(zhí)行 increment()
或 decrement()
方法,避免并發(fā)訪問
分布式鎖實(shí)現(xiàn)
分布式鎖是在分布式系統(tǒng)中用于實(shí)現(xiàn)資源的互斥訪問的一種機(jī)制。下面介紹兩種常見的分布式鎖實(shí)現(xiàn)方法:
- 基于數(shù)據(jù)庫的分布式鎖: 使用數(shù)據(jù)庫作為分布式鎖的持久化存儲(chǔ),通過在表中創(chuàng)建唯一索引或使用悲觀鎖來確保只有一個(gè)節(jié)點(diǎn)能夠成功獲取鎖。
// 獲取分布式鎖 public boolean acquireLock(String lockName) { try (Connection connection = dataSource.getConnection()) { String sql = "INSERT INTO distributed_lock (lock_name) VALUES (?)"; PreparedStatement statement = connection.prepareStatement(sql); statement.setString(1, lockName); int affectedRows = statement.executeUpdate(); return affectedRows > 0; } catch (SQLException e) { // 處理異常 } return false; } // 釋放分布式鎖 public void releaseLock(String lockName) { try (Connection connection = dataSource.getConnection()) { String sql = "DELETE FROM distributed_lock WHERE lock_name = ?"; PreparedStatement statement = connection.prepareStatement(sql); statement.setString(1, lockName); statement.executeUpdate(); } catch (SQLException e) { // 處理異常 } }
在上述示例中,使用數(shù)據(jù)庫表 distributed_lock
存儲(chǔ)分布式鎖的狀態(tài)。
通過執(zhí)行相應(yīng)的SQL語句來獲取鎖和釋放鎖,確保只有一個(gè)節(jié)點(diǎn)能夠插入對(duì)應(yīng)的鎖名稱到表中。其他節(jié)點(diǎn)嘗試插入同樣的鎖名稱會(huì)因?yàn)槲ㄒ凰饕虮^鎖失敗,從而實(shí)現(xiàn)了互斥訪問。
- 基于Redis的分布式鎖: 使用Redis作為分布式鎖的存儲(chǔ)中心,利用Redis的原子操作特性和過期時(shí)間來實(shí)現(xiàn)分布式鎖的獲取和釋放。
public boolean acquireLock(String lockKey, String requestId, int expireTime) { try (Jedis jedis = jedisPool.getResource()) { String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime); return "OK".equalsIgnoreCase(result); } catch (Exception e) { // 處理異常 } return false; } public void releaseLock(String lockKey, String requestId) { try (Jedis jedis = jedisPool.getResource()) { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); } catch (Exception e) { // 處理異常 } }
在上述示例中,使用Redis的 set
命令以原子方式將鎖信息存儲(chǔ)到Redis中,并設(shè)置過期時(shí)間。
通過獲取鎖時(shí)傳遞一個(gè)唯一的請(qǐng)求標(biāo)識(shí)符 requestId
,并在釋放鎖時(shí)校驗(yàn)該標(biāo)識(shí)符,確保只有持有鎖的節(jié)點(diǎn)能夠釋放它。
這兩種分布式鎖的實(shí)現(xiàn)方法各有優(yōu)劣,選擇合適的方式取決于具體業(yè)務(wù)場(chǎng)景和系統(tǒng)需求。分布式鎖的設(shè)計(jì)和實(shí)現(xiàn)需要考慮到分布式環(huán)境下的并發(fā)性、可靠性和性能等方面的問題,確保在多個(gè)節(jié)點(diǎn)之間實(shí)現(xiàn)正確的資源互斥訪問。
到此這篇關(guān)于Java中的分布式鎖與同步鎖使用詳解的文章就介紹到這了,更多相關(guān)分布式鎖與同步鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot整合WebService服務(wù)的實(shí)現(xiàn)代碼
WebService是一個(gè)SOA(面向服務(wù)的編程)的架構(gòu),它是不依賴于語言,不依賴于平臺(tái),可以實(shí)現(xiàn)不同的語言間的相互調(diào)用,通過Internet進(jìn)行基于Http協(xié)議的網(wǎng)絡(luò)應(yīng)用間的交互,這篇文章主要介紹了SpringBoot整合WebService服務(wù)的實(shí)例代碼,需要的朋友可以參考下2022-02-02MybatisPlus特殊查詢的實(shí)現(xiàn)介紹
這篇文章主要介紹了MybatisPlus查詢投影、聚合查詢、分組查詢、等值查詢、范圍查詢、模糊查詢、排序查詢,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-10-10類添加注解@RequestMapping報(bào)錯(cuò)HTTP Status 404的解決
這篇文章主要介紹了類添加注解@RequestMapping報(bào)錯(cuò)HTTP Status 404的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Springboot啟動(dòng)停止命令的.sh腳本編寫方式
這篇文章主要介紹了Springboot啟動(dòng)停止命令的.sh腳本編寫方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05CountDownLatch和Atomic原子操作類源碼解析
這篇文章主要為大家介紹了CountDownLatch和Atomic原子操作類的源碼解析以及理解應(yīng)用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03Spring框架JdbcTemplate數(shù)據(jù)庫事務(wù)管理完全注解方式
這篇文章主要介紹了Spring框架JdbcTemplate數(shù)據(jù)庫事務(wù)管理及完全注解方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05webuploader 實(shí)現(xiàn)圖片批量上傳功能附實(shí)例代碼
這篇文章主要介紹了webuploader 實(shí)現(xiàn)圖片批量上傳功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-11-11Java虛擬機(jī)執(zhí)行引擎知識(shí)總結(jié)
這篇文章主要介紹了有關(guān)Java虛擬機(jī)執(zhí)行引擎的知識(shí),文中實(shí)例簡(jiǎn)單易懂,方便大家更好的學(xué)習(xí),有興趣的朋友可以了解下2020-06-06