最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java中常見的死鎖以及解決方法代碼

 更新時(shí)間:2019年03月13日 11:29:38   作者:diligence-zpf  
這篇文章主要介紹了java中常見的死鎖以及解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

在java中我們常常使用加鎖機(jī)制來確保線程安全,但是如果過度使用加鎖,則可能導(dǎo)致鎖順序死鎖。同樣,我們使用線程池和信號(hào)量來限制對(duì)資源的使用,但是這些被限制的行為可能會(huì)導(dǎo)致資源死鎖。java應(yīng)用程序無法從死鎖中恢復(fù)過來,因此設(shè)計(jì)時(shí)一定要排序那些可能導(dǎo)致死鎖出現(xiàn)的條件。

1.一個(gè)最簡單的死鎖案例
當(dāng)一個(gè)線程永遠(yuǎn)地持有一個(gè)鎖,并且其他線程都嘗試獲得這個(gè)鎖時(shí),那么它們將永遠(yuǎn)被阻塞。在線程A持有鎖L并想獲得鎖M的同時(shí),線程B持有鎖M并嘗試獲得鎖L,那么這兩個(gè)線程將永遠(yuǎn)地等待下去。這種就是最簡答的死鎖形式(或者叫做"抱死")。

2.鎖順序死鎖

如圖:leftRight和rightLeft這兩個(gè)方法分別獲得left鎖和right鎖。如果一個(gè)線程調(diào)用了leftRight,而另一個(gè)線程調(diào)用了rightLeft,并且這兩個(gè)線程的操作是交互執(zhí)行,那么它們就會(huì)發(fā)生死鎖。

死鎖的原因就是兩個(gè)線程試圖以不同的順序來獲得相同的鎖。所以,如果所有的線程以固定的順序來獲得鎖,那么在程序中就不會(huì)出現(xiàn)鎖順序死鎖的問題。

2.1.動(dòng)態(tài)的鎖順序死鎖

我以一個(gè)經(jīng)典的轉(zhuǎn)賬案例來進(jìn)行說明,我們知道轉(zhuǎn)賬就是將資金從一個(gè)賬戶轉(zhuǎn)入另一個(gè)賬戶。在開始轉(zhuǎn)賬之前,首先需要獲得這兩個(gè)賬戶對(duì)象得鎖,以確保通過原子方式來更新兩個(gè)賬戶中的余額,同時(shí)又不破壞一些不變形條件,例如 賬戶的余額不能為負(fù)數(shù)。

所以寫出的代碼如下:

//動(dòng)態(tài)的鎖的順序死鎖
public class DynamicOrderDeadlock {
	
	public static void transferMoney(Account fromAccount,Account toAccount,int amount,int from_index,int to_index) throws Exception {
		System.out.println("賬戶 "+ from_index+"~和賬戶~"+to_index+" ~請(qǐng)求鎖");
		
		synchronized (fromAccount) {
			System.out.println("	賬戶 >>>"+from_index+" <<<獲得鎖");
			synchronized (toAccount) {
				System.out.println("		  賬戶   "+from_index+" & "+to_index+"都獲得鎖");
				if (fromAccount.compareTo(amount) < 0) {
					throw new Exception();
				}else {
					fromAccount.debit(amount);
					toAccount.credit(amount);
				}
			}
		}
	} 
	static class Account {
		private int balance = 100000;//這里假設(shè)每個(gè)人賬戶里面初始化的錢
		private final int accNo;
		private static final AtomicInteger sequence = new AtomicInteger();
		
		public Account() {
			accNo = sequence.incrementAndGet();
		}
		
		void debit(int m) throws InterruptedException {
			Thread.sleep(5);//模擬操作時(shí)間
			balance = balance + m;
		}
		
		void credit(int m) throws InterruptedException {
			Thread.sleep(5);//模擬操作時(shí)間
			balance = balance - m;
		} 
		
		int getBalance() {
			return balance;
		}
		
		int getAccNo() {
			return accNo;
		}
		
		public int compareTo(int money) {
			if (balance > money) {
				return 1;
			}else if (balance < money) {
				return -1;
			}else {
				return 0;
			}
		}
	}
}
public class DemonstrateDeadLock {
	private static final int NUM_THREADS = 5;
	private static final int NUM_ACCOUNTS = 5;
	private static final int NUM_ITERATIONS = 100000;

	public static void main(String[] args) {
		final Random rnd = new Random();
		final Account[] accounts = new Account[NUM_ACCOUNTS];
		
		for(int i = 0;i < accounts.length;i++) {
			accounts[i] = new Account();
		}
		
		
		class TransferThread extends Thread{
			@Override
			public void run() {
				for(int i = 0;i < NUM_ITERATIONS;i++) { 
					int fromAcct = rnd.nextInt(NUM_ACCOUNTS);
					int toAcct =rnd.nextInt(NUM_ACCOUNTS);
					int amount = rnd.nextInt(100);
					try {
						DynamicOrderDeadlock.transferMoney(accounts[fromAcct],accounts[toAcct], amount,fromAcct,toAcct);
							//InduceLockOrder.transferMoney(accounts[fromAcct],accounts[toAcct], amount);
						 //InduceLockOrder2.transferMoney(accounts[fromAcct],accounts[toAcct], amount);
					}catch (Exception e) {
						System.out.println("發(fā)生異常-------"+e);
					}
				}
			}
		}
		 
		for(int i = 0;i < NUM_THREADS;i++) {
			new TransferThread().start();
		}
	}

}

打印結(jié)果如下:
注意:這里的結(jié)果是我把已經(jīng)執(zhí)行完的給刪除后,只剩下導(dǎo)致死鎖的請(qǐng)求.

解決問題方案:定義鎖的順序,并且整個(gè)應(yīng)用中都按照這個(gè)順序來獲取鎖。

方案一

使用System.identityHashCode方法,該方法返回有Object.hashCode返回的值,此時(shí)可以通過某種任意方法來決定鎖的順序。但是在極少數(shù)情況下,兩個(gè)對(duì)象可能擁有相同的散列值,在這種情況下,通過給公共變量加鎖來實(shí)現(xiàn)給鎖制定順序。所以這種方法也是用最小的代價(jià),換來了最大的安全性。
具體代碼如下:

//通過鎖順序來避免死鎖
public class InduceLockOrder {
	private static final Object tieLock = new Object();

	public static void transferMoney(final Account fromAcct, final Account toAcct, final int amount)
			throws Exception {

		class Helper {
			public void transfer() throws Exception {
				if (fromAcct.compareTo(amount) < 0) {
					throw new Exception();
				} else {
					fromAcct.debit(amount);
					toAcct.credit(amount);
				}
			}
		}

		int fromHash = System.identityHashCode(fromAcct);
		int toHash = System.identityHashCode(toAcct);

		if (fromHash < toHash) {
			synchronized (fromAcct) {
				synchronized (toAcct) {
					new Helper().transfer();
				}
			}
		} else if (fromHash > toHash) {
			synchronized (toAcct) {
				synchronized (fromAcct) {
					new Helper().transfer();
				}
			}
		} else {
			synchronized (tieLock) {
				synchronized (fromAcct) {
					synchronized (toAcct) {
						new Helper().transfer();
					}
				}
			}
		}
	}

	
	static class Account {
		private int balance = 100000;
		public Account() {

		}
		
		void debit(int m) throws InterruptedException {
			Thread.sleep(5);
			balance = balance + m;
		}
		
		void credit(int m) throws InterruptedException {
			Thread.sleep(5);
			balance = balance - m;
		} 
		
		int getBalance() {
			return balance;
		}
		public int compareTo(int money) {
			if (balance > money) {
				return 1;
			}else if (balance < money) {
				return -1;
			}else {
				return 0;
			}
		}
		
	}

}

經(jīng)過我測試,此方案可行,不會(huì)造成死鎖。

方案二

在Account中包含一個(gè)唯一的,不可變的,值。比如說賬號(hào)等。通過對(duì)這個(gè)值對(duì)對(duì)象進(jìn)行排序。
具體代碼如下

public class InduceLockOrder2 {

	public static void transferMoney(final Account fromAcct, final Account toAcct, final int amount)
			throws Exception {

		class Helper {
			public void transfer() throws Exception {
				if (fromAcct.compareTo(amount) < 0) {
					throw new Exception();
				} else {
					fromAcct.debit(amount);
					toAcct.credit(amount);
				}
			}
		}

		int fromHash = fromAcct.getAccNo();
		int toHash = toAcct.getAccNo();

		if (fromHash < toHash) {
			synchronized (fromAcct) {
				synchronized (toAcct) {
					new Helper().transfer();
				}
			}
		} else if (fromHash > toHash) {
			synchronized (toAcct) {
				synchronized (fromAcct) {
					new Helper().transfer();
				}
			}
		} 
	}

	
	static class Account {
		private int balance = 100000;
		private final int accNo;
		private static final AtomicInteger sequence = new AtomicInteger();
		
		public Account() {
			accNo = sequence.incrementAndGet();
		}
		
		void debit(int m) throws InterruptedException {
			Thread.sleep(6);
			balance = balance + m;
		}
		
		void credit(int m) throws InterruptedException {
			Thread.sleep(6);
			balance = balance - m;
		} 
		
		int getBalance() {
			return balance;
		}
		
		int getAccNo() {
			return accNo;
		}
		public int compareTo(int money) {
			if (balance > money) {
				return 1;
			}else if (balance < money) {
				return -1;
			}else {
				return 0;
			}
		}
		
	}
}

經(jīng)過測試此方案也可行。

2.2在協(xié)作對(duì)象之間發(fā)生的死鎖
如果在持有鎖時(shí)調(diào)用某外部的方法,那么將出現(xiàn)活躍性問題。在這個(gè)外部方法中可能會(huì)獲取其他的鎖(這個(gè)可能產(chǎn)生死鎖),或阻塞時(shí)間過長,導(dǎo)致其他線程無法及時(shí)獲得當(dāng)前持有的鎖。

場景如下:Taxi代表出租車對(duì)象,包含當(dāng)前位置和目的地。Dispatcher代表車隊(duì)。當(dāng)一個(gè)線程收到GPS更新事件時(shí)掉用setLocation,那么它首先更新出租車的位置,然后判斷它是否到達(dá)目的地。如果已經(jīng)到達(dá),它會(huì)通知Dispatcher:它需要一個(gè)新的目的地。因?yàn)閟etLocation和notifyAvailable都是同步方法,因此掉用setLocation線程首先獲取taxi的鎖,然后在獲取Dispatcher的鎖。同樣,掉用getImage的線程首先獲取Dispatcher的鎖,再獲取每一個(gè)taxi的鎖,這兩個(gè)線程按照不同的順序來獲取鎖,因此可能導(dǎo)致死鎖。

能造成死鎖的代碼如下:

//會(huì)發(fā)生死鎖
public class CooperatingDeadLock {

	// 坐標(biāo)類
	class Point {
		private final int x;
		private final int y;

		public Point(int x, int y) {
			this.x = x;
			this.y = y;
		}

		public int getX() {
			return x;
		}

		public int getY() {
			return y;
		}
	}

	// 出租車類
	class Taxi {
		private Point location, destination;
		private final Dispatcher dispatcher;
		
		public Taxi(Dispatcher dispatcher) {
			this.dispatcher = dispatcher;
		}
		
		public synchronized Point getLocation() {
			return location;
		}
		
		
		public synchronized void setLocation(Point location) {
			this.location = location;
			if (location.equals(destination)) {
				dispatcher.notifyAvailable(this);
			}
		}
		
		
		public synchronized Point getDestination() {
			return destination;
		}
		
		public synchronized void setDestination(Point destination) {
			this.destination = destination;
		}
	}

	class Dispatcher {
		private final Set<Taxi> taxis;
		private final Set<Taxi> availableTaxis;

		public Dispatcher() {
			taxis = new HashSet<>();
			availableTaxis = new HashSet<>();
		}
		
		public synchronized void notifyAvailable(Taxi taxi) {
			availableTaxis.add(taxi);
		}

		public synchronized Image getImage() {
			Image image = new Image();
			for(Taxi t:taxis) {
				image.drawMarker(t.getLocation());
			}
			return image;
		}
	}
	
	class Image{
		public void drawMarker(Point p) {
			
		}
	}

}

解決方案:使用開放掉用。
如果再調(diào)用某個(gè)方法時(shí)不需要持有鎖,那么這種調(diào)用就被稱為開放掉用。這種調(diào)用能有效的避免死鎖,并且易于分析線程安全。

修改后的代碼如下:

//此方案不會(huì)造成死鎖
public class CooperatingNoDeadlock {
	// 坐標(biāo)類
		class Point {
			private final int x;
			private final int y;

			public Point(int x, int y) {
				this.x = x;
				this.y = y;
			}

			public int getX() {
				return x;
			}

			public int getY() {
				return y;
			}
		}

		// 出租車類
		class Taxi {
			private Point location, destination;
			private final Dispatcher dispatcher;
			
			public Taxi(Dispatcher dispatcher) {
				this.dispatcher = dispatcher;
			}
			
			public synchronized Point getLocation() {
				return location;
			}
			
			
			public void setLocation(Point location) {
				boolean reachedDestination;
				synchronized (this) {
					this.location = location;
					reachedDestination = location.equals(destination);
				}
				if (reachedDestination) {
					dispatcher.notifyAvailable(this);
				}
			}
			
			
			public synchronized Point getDestination() {
				return destination;
			}
			
			public synchronized void setDestination(Point destination) {
				this.destination = destination;
			}
		}

		class Dispatcher {
			private final Set<Taxi> taxis;
			private final Set<Taxi> availableTaxis;

			public Dispatcher() {
				taxis = new HashSet<>();
				availableTaxis = new HashSet<>();
			}
			
			public synchronized void notifyAvailable(Taxi taxi) {
				availableTaxis.add(taxi);
			}

			public Image getImage() {
				Set<Taxi> copy;
				synchronized (this) {
					copy = new HashSet<>(taxis);
				}
				
				Image image = new Image();
				for(Taxi t:copy) {
					image.drawMarker(t.getLocation());
				}
				return image;
			}
			
			
		}
		
		class Image{
			public void drawMarker(Point p) {
				
			}
		}
}

總結(jié):活躍性故障是一個(gè)非常嚴(yán)重的問題,因?yàn)楫?dāng)出現(xiàn)活躍性故障時(shí),除了終止應(yīng)用程序之外沒有其他任何機(jī)制可以幫助從這種故障中恢復(fù)過來。最常見的活躍性故障就是鎖順序死鎖。在設(shè)計(jì)時(shí)應(yīng)該避免產(chǎn)生順序死鎖:確保線程在獲取多個(gè)鎖時(shí)采用一直的順序。最好的解決方案是在程序中始終使用開放掉用。這將大大減小需要同時(shí)持有多個(gè)鎖的地方,也更容易發(fā)現(xiàn)這些地方。

以上所述是小編給大家介紹的java中常見的死鎖以及解決方法詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

  • JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)原理解析

    JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)原理解析

    這篇文章主要介紹了JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • SpringBoot修改子模塊Module的jdk版本的方法 附修改原因

    SpringBoot修改子模塊Module的jdk版本的方法 附修改原因

    這篇文章主要介紹了SpringBoot修改子模塊Module的jdk版本的方法 附修改原因,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • 淺析SpringCloud Alibaba-Nacos 作為注冊(cè)中心示例代碼

    淺析SpringCloud Alibaba-Nacos 作為注冊(cè)中心示例代碼

    這篇文章主要介紹了SpringCloud Alibaba-Nacos 作為注冊(cè)中心示例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Java使用正則表達(dá)式獲取子文本的方法示例

    Java使用正則表達(dá)式獲取子文本的方法示例

    這篇文章主要介紹了Java使用正則表達(dá)式獲取子文本的方法,結(jié)合實(shí)例形式分析了java針對(duì)子文本的正則操作相關(guān)函數(shù)與使用技巧,需要的朋友可以參考下
    2017-09-09
  • Java easyexcel導(dǎo)出報(bào)內(nèi)存溢出的問題解決

    Java easyexcel導(dǎo)出報(bào)內(nèi)存溢出的問題解決

    在Java開發(fā)時(shí),使用EasyExcel處理大數(shù)據(jù)量導(dǎo)出可能遇到內(nèi)存溢出問題,本文深入分析了內(nèi)存溢出的原因,并提出了優(yōu)化策略,感興趣的可以了解一下
    2024-10-10
  • 為什么Java開發(fā)需要配置環(huán)境變量

    為什么Java開發(fā)需要配置環(huán)境變量

    這篇文章主要介紹了為什么Java開發(fā)需要配置環(huán)境變量,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-08-08
  • java maven項(xiàng)目如何讀取配置文件信息

    java maven項(xiàng)目如何讀取配置文件信息

    這篇文章主要介紹了java maven項(xiàng)目如何讀取配置文件信息,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 基于SpringBoot + Redis實(shí)現(xiàn)密碼暴力破解防護(hù)

    基于SpringBoot + Redis實(shí)現(xiàn)密碼暴力破解防護(hù)

    在現(xiàn)代應(yīng)用程序中,保護(hù)用戶密碼的安全性是至關(guān)重要的,密碼暴力破解是指通過嘗試多個(gè)密碼組合來非法獲取用戶賬戶的密碼,為了保護(hù)用戶密碼不被暴力破解,我們可以使用Spring Boot和Redis來實(shí)現(xiàn)一些防護(hù)措施,本文將介紹如何利用這些技術(shù)來防止密碼暴力破解攻擊
    2023-06-06
  • 你真的了解java單例模式了嗎?

    你真的了解java單例模式了嗎?

    這篇文章主要介紹了你真的了解java單例模式了嗎?實(shí)際上單例模式有著好幾個(gè)變種,并且多線程中涉及到線程安全問題,,需要的朋友可以參考下
    2019-06-06
  • java正則替換括號(hào)中的逗號(hào)實(shí)現(xiàn)示例

    java正則替換括號(hào)中的逗號(hào)實(shí)現(xiàn)示例

    本文主要介紹了java正則替換括號(hào)中的逗號(hào)實(shí)現(xiàn)示例,主要介紹了兩種示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-01-01

最新評(píng)論

安阳市| 临高县| 嘉鱼县| 菏泽市| 连州市| 南开区| 永顺县| 兴化市| 清丰县| 日土县| 南投县| 大宁县| 永清县| 彩票| 嘉兴市| 楚雄市| 察隅县| 彭山县| 蕉岭县| 巴南区| 台州市| 武功县| 安平县| 黔西县| 张家港市| 吉水县| 法库县| 黄冈市| 洛隆县| 电白县| 宣恩县| 东阿县| 晋州市| 桃园县| 沽源县| 临沧市| 射洪县| 大港区| 普陀区| 景宁| 青州市|