Java如何避免死鎖和競(jìng)態(tài)條件的實(shí)現(xiàn)
在Java SE中,多線程編程是非常常見(jiàn)的。然而,多線程編程也會(huì)帶來(lái)一些問(wèn)題,比如死鎖和競(jìng)態(tài)條件。本文將介紹如何避免這些問(wèn)題。
死鎖
死鎖是指兩個(gè)或多個(gè)線程互相等待對(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è)例子中,線程1先獲取了鎖1,然后等待1秒鐘,接著嘗試獲取鎖2。而線程2先獲取了鎖2,然后等待1秒鐘,接著嘗試獲取鎖1。由于兩個(gè)線程互相等待對(duì)方釋放鎖,因此程序會(huì)一直卡在那里,無(wú)法繼續(xù)執(zhí)行。
為了避免死鎖,我們需要遵循以下規(guī)則:
- 避免嵌套鎖。如果一個(gè)線程已經(jīng)持有了一個(gè)鎖,那么它就不能再去獲取另一個(gè)鎖。
- 避免持有鎖的時(shí)間過(guò)長(zhǎng)。如果一個(gè)線程持有鎖的時(shí)間過(guò)長(zhǎng),那么其他線程就會(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è)線程成功獲取了鎖1和鎖2后,就可以執(zhí)行相應(yīng)的操作了。在釋放鎖的時(shí)候,我們需要將鎖的引用設(shè)置為null,并調(diào)用notify()方法來(lái)喚醒其他線程。
競(jìng)態(tài)條件
競(jìng)態(tài)條件是指多個(gè)線程同時(shí)訪問(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è)線程,它們分別對(duì)count變量進(jìn)行100000次加1操作。由于這兩個(gè)線程是并發(fā)執(zhí)行的,因此它們可能會(huì)同時(shí)訪問(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變量的訪問(wèn)是同步的。當(dāng)一個(gè)線程獲取了鎖之后,其他線程就必須等待該線程釋放鎖之后才能獲取鎖。這樣就可以避免競(jìng)態(tài)條件了。
總結(jié)
在Java SE中,多線程編程是非常常見(jià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)文章
基于HttpServletRequest 相關(guān)常用方法的應(yīng)用
本篇文章小編為大家介紹,基于HttpServletRequest 相關(guān)常用方法的應(yīng)用,需要的朋友參考下2013-04-04java并發(fā)編程之深入理解Synchronized的使用
文詳細(xì)講述了線程、進(jìn)程的關(guān)系及在操作系統(tǒng)中的表現(xiàn),這是多線程學(xué)習(xí)必須了解的基礎(chǔ)。本文將接著講一下Java線程同步中的一個(gè)重要的概念synchronized,希望能夠給你有所幫助2021-06-06Java中的CGLIB動(dòng)態(tài)代理的使用及原理詳解
這篇文章主要介紹了Java中的CGLIB動(dòng)態(tài)代理的使用及原理詳解,CGLIB是一個(gè)功能強(qiáng)大,高性能的代碼生成包,它為沒(méi)有實(shí)現(xiàn)接口的類提供代理,為JDK的動(dòng)態(tài)代理提供了很好的補(bǔ)充,需要的朋友可以參考下2023-09-09MyBatis-plus使用lambda條件構(gòu)造器報(bào)錯(cuò)問(wèn)題及解決
這篇文章主要介紹了MyBatis-plus使用lambda條件構(gòu)造器報(bào)錯(cuò)問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01springBoot server.port=-1的含義說(shuō)明
這篇文章主要介紹了springBoot server.port=-1的含義說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08