所謂死鎖: 是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過(guò)程中杨幼,由于競(jìng)爭(zhēng)資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無(wú)外力作用四瘫,它們都將無(wú)法推進(jìn)下去欲逃。此時(shí)稱(chēng)系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖,這些永遠(yuǎn)在互相等待的進(jìn)程稱(chēng)為死鎖進(jìn)程洗做。
百度百科
我們使用加鎖機(jī)制來(lái)確保線(xiàn)程安全,但如果過(guò)度使用加鎖,則可能導(dǎo)致鎖順序死鎖(Lock-Ordering Deadlock).同樣,我們使用線(xiàn)程池和信號(hào)量來(lái)限制對(duì)資源的使用,但這些被限制的行為可能會(huì)導(dǎo)致資源死鎖(Resource DeadLock).
Java應(yīng)用程序無(wú)法從死鎖中恢復(fù)過(guò)來(lái),因此在程序設(shè)計(jì)時(shí)要排除那些可能導(dǎo)致死鎖出現(xiàn)的條件
下面對(duì)死鎖的一些簡(jiǎn)單介紹
一,死鎖示例
1,鎖順序死鎖
- 線(xiàn)程以不同的順序來(lái)獲得相同的鎖,那么就可能出現(xiàn)死鎖
- 若所有線(xiàn)程以固定的順序來(lái)獲得鎖,那么在程序中就不會(huì)出現(xiàn)鎖順序死鎖的問(wèn)題
示例:
public class Test {
public static void main(String[] args) {
LeftRightDeadLock deadLock = new LeftRightDeadLock();
LeftRightThread leftRightThread = new LeftRightThread(deadLock);
RightLeftThread rightLeftThread = new RightLeftThread(deadLock);
leftRightThread.start();
rightLeftThread.start();
}
}
class LeftRightThread extends Thread{
private LeftRightDeadLock deadLock;
public LeftRightThread(LeftRightDeadLock deadLock) {
this.deadLock = deadLock;
}
@Override
public void run() {
while(true){
deadLock.leftRight();
}
}
}
class RightLeftThread extends Thread{
private LeftRightDeadLock deadLock;
public RightLeftThread(LeftRightDeadLock deadLock) {
this.deadLock = deadLock;
}
@Override
public void run() {
while(true){
deadLock.rightLeft();
}
}
}
class LeftRightDeadLock{
private Object leftLock = new Object();
private Object rightLock = new Object();
public void leftRight(){
synchronized (leftLock) {
synchronized (rightLock) {
System.out.println(Thread.currentThread().getName()+" leftRight");
}
}
}
public void rightLeft(){
synchronized (rightLock) {
synchronized (leftLock) {
System.out.println(Thread.currentThread().getName()+" rightLeft");
}
}
}
通過(guò)讓線(xiàn)程獲取鎖的順序一致來(lái)避免死鎖
class LeftRightDeadLock{
private Object leftLock = new Object();
private Object rightLock = new Object();
public void leftRight(){
synchronized (leftLock) {
synchronized (rightLock) {
System.out.println(Thread.currentThread().getName()+" leftRight");
}
}
}
public void rightLeft(){
synchronized (leftLock) {
synchronized (rightLock) {
System.out.println(Thread.currentThread().getName()+" rightLeft");
}
}
}
2,在協(xié)作對(duì)象之間發(fā)生的死鎖
- 如果在持有鎖時(shí)調(diào)用某個(gè)外部方法,那么將出現(xiàn)活躍性問(wèn)題.在這個(gè)外部方法中可能會(huì)獲取其他鎖(這可能會(huì)產(chǎn)生死鎖),或者阻塞時(shí)間過(guò)長(zhǎng),導(dǎo)致其他線(xiàn)程無(wú)法及時(shí)獲得當(dāng)前被持有的鎖
- 解決辦法---開(kāi)放調(diào)用
如果在調(diào)用某個(gè)方法時(shí)不需要持有鎖,那么這種調(diào)用被稱(chēng)為開(kāi)放調(diào)用(Open Call)
public class Test {
public static void main(String[] args) throws InterruptedException {
First first = null;
Second second = null;
first = new First();
second = new Second();
first.setSecond(second);
second.setFirst(first);
FirstThread firstThread = new FirstThread(first);
SecondThread secondThread = new SecondThread(second);
firstThread.start();
Thread.sleep(1000);
secondThread.start();
}
}
class FirstThread extends Thread{
First first;
FirstThread(First first){
this.first = first;
}
@Override
public void run() {
for(;;){
first.getFirst();
}
}
}
class SecondThread extends Thread{
Second second;
SecondThread(Second second){
this.second = second;
}
@Override
public void run() {
for(;;){
second.getSecond();
}
}
}
class First {
private Second second;
public synchronized String getFirst() {
second.setFirst(this);
System.out.println(Thread.currentThread());
return "first";
}
public synchronized void setSecond(Second second){
this.second = second;
}
}
class Second {
private First first;
public synchronized String getSecond() {
first.setSecond(this);
System.out.println(Thread.currentThread());
return "second";
}
public synchronized void setFirst(First first){
this.first = first;
}
}
解決后的代碼
class First {
private Second second;
public synchronized String getFirst() {
second.setFirst(this);
synchronized (this) {
System.out.println(Thread.currentThread());
}
return "first";
}
public synchronized void setSecond(Second second){
this.second = second;
}
}
class Second {
private First first;
public String getSecond() {
first.setSecond(this);
synchronized (this) {
System.out.println(Thread.currentThread());
}
return "second";
}
public synchronized void setFirst(First first){
this.first = first;
}
}
3,資源死鎖
當(dāng)多個(gè)線(xiàn)程相互持有彼此正在等待的鎖而又不釋放自己已持有的鎖時(shí)會(huì)發(fā)生死鎖,當(dāng)它們?cè)谙嗤馁Y源集合上等待時(shí),也會(huì)發(fā)生死鎖.
二,死鎖的避免和診斷
1,死鎖的避免
使用Lock類(lèi)定時(shí)的tryLock功能來(lái)代替內(nèi)置鎖機(jī)制,可以檢測(cè)死鎖和從死鎖中回復(fù)過(guò)來(lái).
當(dāng)使用內(nèi)置鎖時(shí),只有沒(méi)有獲得鎖,就會(huì)永遠(yuǎn)等待下去,而顯示鎖則可以指定一個(gè)超時(shí)時(shí)限,在等待超過(guò)該時(shí)間后tryLock會(huì)返回一個(gè)失敗信息.
2,通過(guò)線(xiàn)程轉(zhuǎn)儲(chǔ)信息來(lái)分析死鎖
JVM可以通過(guò)線(xiàn)程轉(zhuǎn)儲(chǔ)(Thread Dump)來(lái)幫助識(shí)別死鎖的發(fā)生.
線(xiàn)程轉(zhuǎn)儲(chǔ)包括各個(gè)運(yùn)行中的線(xiàn)程的棧追蹤信息,這類(lèi)似于發(fā)生異常時(shí)的棧追蹤信息.線(xiàn)程轉(zhuǎn)儲(chǔ)還包括加鎖信息,例如每個(gè)線(xiàn)程持有了那些鎖,在那些棧幀中獲得這些鎖,以及被阻塞的線(xiàn)程正在等待獲取哪一個(gè)鎖.
以第一個(gè)死鎖示例(鎖順序死鎖)
輸入命令: jstack -l 16601
其中16601是進(jìn)程號(hào)
下面是部分的線(xiàn)程轉(zhuǎn)儲(chǔ)信息
........
........
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007fa04381beb8 (object 0x00000007aaadd8b0, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007fa04381bf68 (object 0x00000007aaadd8c0, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at LeftRightDeadLock.rightLeft(Test.java:54)
- waiting to lock <0x00000007aaadd8b0> (a java.lang.Object)
- locked <0x00000007aaadd8c0> (a java.lang.Object)
at RightLeftThread.run(Test.java:35)
"Thread-0":
at LeftRightDeadLock.leftRight(Test.java:46)
- waiting to lock <0x00000007aaadd8c0> (a java.lang.Object)
- locked <0x00000007aaadd8b0> (a java.lang.Object)
at LeftRightThread.run(Test.java:21)
Found 1 deadlock.
參考:
<<java編發(fā)編程實(shí)戰(zhàn)>>