Java如何避免死鎖和競(jìng)態(tài)條件的實(shí)現(xiàn)
在Java SE中,多線(xiàn)程編程是非常常見(jiàn)的。然而,多線(xiàn)程編程也會(huì)帶來(lái)一些問(wèn)題,比如死鎖和競(jìng)態(tài)條件。本文將介紹如何避免這些問(wèn)題。
死鎖
死鎖是指兩個(gè)或多個(gè)線(xiàn)程互相等待對(duì)方釋放資源,從而導(dǎo)致程序無(wú)法繼續(xù)執(zhí)行的情況。下面是一個(gè)死鎖的例子:
public class DeadlockExample {
? ? private static Object lock1 = new Object();
? ? private static Object lock2 = new Object();
? ? public static void main(String[] args) {
? ? ? ? Thread t1 = new Thread(() -> {
? ? ? ? ? ? synchronized (lock1) {
? ? ? ? ? ? ? ? System.out.println("Thread 1 acquired lock 1");
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? Thread.sleep(1000);
? ? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? synchronized (lock2) {
? ? ? ? ? ? ? ? ? ? System.out.println("Thread 1 acquired lock 2");
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? Thread t2 = new Thread(() -> {
? ? ? ? ? ? synchronized (lock2) {
? ? ? ? ? ? ? ? System.out.println("Thread 2 acquired lock 2");
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? Thread.sleep(1000);
? ? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? synchronized (lock1) {
? ? ? ? ? ? ? ? ? ? System.out.println("Thread 2 acquired lock 1");
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? t1.start();
? ? ? ? t2.start();
? ? }
}在這個(gè)例子中,線(xiàn)程1先獲取了鎖1,然后等待1秒鐘,接著嘗試獲取鎖2。而線(xiàn)程2先獲取了鎖2,然后等待1秒鐘,接著嘗試獲取鎖1。由于兩個(gè)線(xiàn)程互相等待對(duì)方釋放鎖,因此程序會(huì)一直卡在那里,無(wú)法繼續(xù)執(zhí)行。
為了避免死鎖,我們需要遵循以下規(guī)則:
- 避免嵌套鎖。如果一個(gè)線(xiàn)程已經(jīng)持有了一個(gè)鎖,那么它就不能再去獲取另一個(gè)鎖。
- 避免持有鎖的時(shí)間過(guò)長(zhǎng)。如果一個(gè)線(xiàn)程持有鎖的時(shí)間過(guò)長(zhǎng),那么其他線(xiàn)程就會(huì)被阻塞,從而導(dǎo)致程序性能下降。
- 使用tryLock()方法。tryLock()方法可以嘗試獲取鎖,如果獲取失敗則立即返回,而不是一直等待。
下面是一個(gè)避免死鎖的例子:
public class AvoidDeadlockExample {
? ? private static Object lock1 = new Object();
? ? private static Object lock2 = new Object();
? ? public static void main(String[] args) {
? ? ? ? Thread t1 = new Thread(() -> {
? ? ? ? ? ? boolean acquiredLock1 = false;
? ? ? ? ? ? boolean acquiredLock2 = false;
? ? ? ? ? ? while (!acquiredLock1 || !acquiredLock2) {
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? acquiredLock1 = tryAcquireLock(lock1);
? ? ? ? ? ? ? ? ? ? acquiredLock2 = tryAcquireLock(lock2);
? ? ? ? ? ? ? ? ? ? if (acquiredLock1 && acquiredLock2) {
? ? ? ? ? ? ? ? ? ? ? ? System.out.println("Thread 1 acquired lock 1 and lock 2");
? ? ? ? ? ? ? ? ? ? ? ? // do something
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? Thread.sleep(100);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? } finally {
? ? ? ? ? ? ? ? ? ? if (acquiredLock1) {
? ? ? ? ? ? ? ? ? ? ? ? lock1.notify();
? ? ? ? ? ? ? ? ? ? ? ? lock1 = null;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? if (acquiredLock2) {
? ? ? ? ? ? ? ? ? ? ? ? lock2.notify();
? ? ? ? ? ? ? ? ? ? ? ? lock2 = null;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? Thread t2 = new Thread(() -> {
? ? ? ? ? ? boolean acquiredLock1 = false;
? ? ? ? ? ? boolean acquiredLock2 = false;
? ? ? ? ? ? while (!acquiredLock1 || !acquiredLock2) {
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? acquiredLock1 = tryAcquireLock(lock1);
? ? ? ? ? ? ? ? ? ? acquiredLock2 = tryAcquireLock(lock2);
? ? ? ? ? ? ? ? ? ? if (acquiredLock1 && acquiredLock2) {
? ? ? ? ? ? ? ? ? ? ? ? System.out.println("Thread 2 acquired lock 1 and lock 2");
? ? ? ? ? ? ? ? ? ? ? ? // do something
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? Thread.sleep(100);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? } finally {
? ? ? ? ? ? ? ? ? ? if (acquiredLock1) {
? ? ? ? ? ? ? ? ? ? ? ? lock1.notify();
? ? ? ? ? ? ? ? ? ? ? ? lock1 = null;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? if (acquiredLock2) {
? ? ? ? ? ? ? ? ? ? ? ? lock2.notify();
? ? ? ? ? ? ? ? ? ? ? ? lock2 = null;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? t1.start();
? ? ? ? t2.start();
? ? }
? ? private static boolean tryAcquireLock(Object lock) throws InterruptedException {
? ? ? ? synchronized (lock) {
? ? ? ? ? ? if (lock == lock1 && lock2 == null) {
? ? ? ? ? ? ? ? lock2 = lock;
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? } else if (lock == lock2 && lock1 == null) {
? ? ? ? ? ? ? ? lock1 = lock;
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? lock.wait();
? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? }
? ? ? ? }
? ? }
}在這個(gè)例子中,我們使用了tryAcquireLock()方法來(lái)嘗試獲取鎖。如果獲取成功,則執(zhí)行相應(yīng)的操作;否則等待一段時(shí)間后再次嘗試獲取鎖。當(dāng)一個(gè)線(xiàn)程成功獲取了鎖1和鎖2后,就可以執(zhí)行相應(yīng)的操作了。在釋放鎖的時(shí)候,我們需要將鎖的引用設(shè)置為null,并調(diào)用notify()方法來(lái)喚醒其他線(xiàn)程。
競(jìng)態(tài)條件
競(jìng)態(tài)條件是指多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)共享資源,從而導(dǎo)致程序出現(xiàn)不可預(yù)期的結(jié)果。下面是一個(gè)競(jìng)態(tài)條件的例子:
public class RaceConditionExample {
? ? private static int count = 0;
? ? public static void main(String[] args) {
? ? ? ? Thread t1 = new Thread(() -> {
? ? ? ? ? ? for (int i = 0; i < 100000; i++) {
? ? ? ? ? ? ? ? count++;
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? Thread t2 = new Thread(() -> {
? ? ? ? ? ? for (int i = 0; i < 100000; i++) {
? ? ? ? ? ? ? ? count++;
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? t1.start();
? ? ? ? t2.start();
? ? ? ? try {
? ? ? ? ? ? t1.join();
? ? ? ? ? ? t2.join();
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? System.out.println("Count: " + count);
? ? }
}在這個(gè)例子中,我們創(chuàng)建了兩個(gè)線(xiàn)程,它們分別對(duì)count變量進(jìn)行100000次加1操作。由于這兩個(gè)線(xiàn)程是并發(fā)執(zhí)行的,因此它們可能會(huì)同時(shí)訪(fǎng)問(wèn)count變量,從而導(dǎo)致程序出現(xiàn)不可預(yù)期的結(jié)果。
為了避免競(jìng)態(tài)條件,我們需要使用同步機(jī)制。Java中的同步機(jī)制包括synchronized關(guān)鍵字和Lock接口。下面是一個(gè)使用synchronized關(guān)鍵字的例子:
public class AvoidRaceConditionExample {
? ? private static int count = 0;
? ? public static void main(String[] args) {
? ? ? ? Thread t1 = new Thread(() -> {
? ? ? ? ? ? for (int i = 0; i < 100000; i++) {
? ? ? ? ? ? ? ? synchronized (AvoidRaceConditionExample.class) {
? ? ? ? ? ? ? ? ? ? count++;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? Thread t2 = new Thread(() -> {
? ? ? ? ? ? for (int i = 0; i < 100000; i++) {
? ? ? ? ? ? ? ? synchronized (AvoidRaceConditionExample.class) {
? ? ? ? ? ? ? ? ? ? count++;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? t1.start();
? ? ? ? t2.start();
? ? ? ? try {
? ? ? ? ? ? t1.join();
? ? ? ? ? ? t2.join();
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? System.out.println("Count: " + count);
? ? }
}在這個(gè)例子中,我們使用synchronized關(guān)鍵字來(lái)保證對(duì)count變量的訪(fǎng)問(wèn)是同步的。當(dāng)一個(gè)線(xiàn)程獲取了鎖之后,其他線(xiàn)程就必須等待該線(xiàn)程釋放鎖之后才能獲取鎖。這樣就可以避免競(jìng)態(tài)條件了。
總結(jié)
在Java SE中,多線(xiàn)程編程是非常常見(jiàn)的。然而,多線(xiàn)程編程也會(huì)帶來(lái)一些問(wèn)題,比如死鎖和競(jìng)態(tài)條件。為了避免這些問(wèn)題,我們需要遵循一些規(guī)則,比如避免嵌套鎖、避免持有鎖的時(shí)間過(guò)長(zhǎng)、使用tryLock()方法等。另外,我們還可以使用同步機(jī)制來(lái)避免競(jìng)態(tài)條件。在實(shí)際開(kāi)發(fā)中,我們需要根據(jù)具體的情況選擇合適的方法來(lái)避免這些問(wèn)題。
到此這篇關(guān)于Java如何避免死鎖和競(jìng)態(tài)條件的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Java 避免死鎖和競(jìng)態(tài)條件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA中Maven配置之當(dāng)前項(xiàng)目與新項(xiàng)目的統(tǒng)一設(shè)置過(guò)程
這篇文章主要介紹了IDEA中Maven配置之當(dāng)前項(xiàng)目與新項(xiàng)目的統(tǒng)一設(shè)置過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2026-05-05
Java線(xiàn)程之間數(shù)據(jù)傳遞的實(shí)現(xiàn)示例(4種)
我們經(jīng)常會(huì)遇到父子線(xiàn)程數(shù)據(jù)傳遞(非調(diào)用參數(shù))的場(chǎng)景,本文主要介紹了Java線(xiàn)程之間數(shù)據(jù)傳遞的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08
解決JSONObject.toJSONString()輸出null的問(wèn)題
這篇文章主要介紹了解決JSONObject.toJSONString()輸出null的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
Java中JavaBean對(duì)象和Map的互相轉(zhuǎn)換方法實(shí)例
為什么需要將javaBean和map進(jìn)行轉(zhuǎn)換,在很多應(yīng)用場(chǎng)景中,需要將key=value形式的數(shù)據(jù)與javaBean對(duì)象相互轉(zhuǎn)換,下面這篇文章主要給大家介紹了關(guān)于Java中JavaBean對(duì)象和Map的互相轉(zhuǎn)換的相關(guān)資料,需要的朋友可以參考下2022-11-11
Spring Boot 一個(gè)注解搞定加密 + 解密 + 簽名
本文介紹了一種基于Spring Boot 3.x的接口安全解決方案,通過(guò)自定義注解@ApiSecurity結(jié)合AOP切面,實(shí)現(xiàn)請(qǐng)求解密、驗(yàn)簽、響應(yīng)加密及加簽的自動(dòng)化處理,_springboot api aop 報(bào)文加解密2025-09-09
對(duì)Java的面對(duì)對(duì)象編程中對(duì)象和引用以及內(nèi)部類(lèi)的理解
這篇文章主要介紹了對(duì)Java的面對(duì)對(duì)象編程中對(duì)象和引用以及內(nèi)部類(lèi)的理解,需要的朋友可以參考下2016-01-01
springboot + mybatis-plus實(shí)現(xiàn)多表聯(lián)合查詢(xún)功能(注解方式)
這篇文章主要介紹了springboot + mybatis-plus實(shí)現(xiàn)多表聯(lián)合查詢(xún)功能,是最簡(jiǎn)單的一種注解方式,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
SpringCloud中Sentinel基礎(chǔ)場(chǎng)景和異常處理方式
這篇文章主要介紹了SpringCloud中Sentinel基礎(chǔ)場(chǎng)景和異常處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04

