Java 併發編程:併發中死鎖的造成條件及處理

死鎖是一種無限的互相等待的狀態,兩個或兩個以上的線程或進程構成一個互相等待的環狀。以兩個線程爲例,線程一持有A鎖同時在等待B鎖,而線程二持有B鎖同時在等待A鎖,這就致使兩個線程互相等待沒法往下執行。現實生活中一個經典的死鎖情形就是四輛汽車經過沒有紅綠燈的十字路口,假如四輛車同時到達中心的,那麼它們將造成一個死鎖狀態。每輛車擁有本身車道上的使用權,但同時也在等另一輛汽車讓出另一條道的使用權算法

死鎖的例子

該例子中一共有lock1和lock2兩個鎖。線程一啓動後先嚐試獲取lock1鎖,成功獲取lock1後再繼續嘗試獲取lock2鎖。而線程二則是先嚐試獲取lock2鎖,成功獲取lock2鎖後再繼續嘗試獲取lock1鎖。當咱們某次啓動程序後可能的輸出狀況以下,也就進入了死鎖狀態,但並不是每次都必定會進入死鎖狀態,每一個線程睡眠100毫秒是爲了增長死鎖的可能。最終兩個線程處於互相無線等待狀態,得到lock1的線程一在等lock2,而得到lock2的鎖卻在等lock1。編程

死鎖的處理

因爲死鎖的檢測涉及到不少複雜的場景,並且它仍是運行時纔會產生的,因此編程語言編譯器通常也不會提供死鎖的檢測功能,包括Java也不提供死鎖檢測功能。這其實就叫作鴕鳥算法,對於某件事若是咱們沒有很好的處理方法,那麼就學鴕鳥同樣把頭埋入沙中僞裝什麼都看不見。死鎖的場景處理就交給了實際編程的開發者,開發者須要本身去避免死鎖的發生,或者制定某些措施去處理死鎖發生時的場景。常見的死鎖處理方式大體分爲兩類:一種是事前的預防措施,包括鎖的順序化、資源合併、避免鎖嵌套等等。另外一種是過後的處理措施,包括鎖超時機制、搶佔資源機制、撤銷線程等等。下面咱們詳細看看每種措施的狀況。編程語言

鎖的順序變化

前面說到的死鎖造成的條件中環形條件,咱們能夠破壞這個條件來避免死鎖的發生。具體就是將鎖的獲取進行順序化,全部線程和進程對鎖的獲取都按指定的順序進行,好比下圖中P一、P二、P3三個線程它們都先嚐試持有R1鎖,再嘗試持有R2鎖,最後嘗試持有R3鎖。固然也能夠當作是要獲取R3鎖就必須先獲取R2鎖,而要獲取R2鎖就必須先獲取R1鎖。這樣就能破壞環形條件,從而避免死鎖。spa

資源合併

資源合併的作法就是將多個資源合併當成一個資源來看待,這樣就能將對多個資源的獲取變成只對一個資源的獲取,從而避免了死鎖的發生。以下圖,將資源R一、資源R2和資源R3合併成一個資源R,而後三個線程對其進行獲取操做。線程

避免鎖嵌套

鎖獲取操做的嵌套行爲纔可能致使死鎖發生,因此咱們能夠經過去除鎖嵌套來避免死鎖。每一個線程都是使用完某個資源就釋放,而後才能再獲取另一個資源,並且使用完又進行釋放,這就是去除鎖嵌套。以下圖中線程P1持有R1鎖後釋放,而後持有R2鎖後釋放,最後持有R3鎖並釋放,其它線程也是相似地操做。進程

鎖的超時機制

過後處理的第一種措施是鎖超時機制,核心就在於對鎖的等待並不是永久的而是有超時的,某個線程對某個鎖的等待若是超過了指定的時間則作超時處理,直接結束掉該線程。好比下圖中,三個線程已經進入死鎖狀態了,假如線程P1等待R2鎖的時間超過了超時時間,此時P1將結束而且釋放對R1鎖的佔有權。這時線程P3則可以獲取到R1鎖,因而可以解除等待,自此解除了死鎖狀態。資源

總結

本文主要介紹了死鎖相關內容,除了介紹死鎖概念外咱們還提供了死鎖的例子,還有死鎖造成的條件,以及死鎖的處理方式。死鎖的處理主要包括鎖的順序化、資源合併、避免鎖嵌套等事前預防措施和超時機制、搶佔資源機制、撤銷線程機制等事中的處理措施開發

相關文章
相關標籤/搜索