死锁是指两个或者多个线程在执行过程中,因争夺资源而造成的一种相互等待的现象。也就是说每个线程已经获取了其中一个对象上的锁,而且正在等待另一个对象上的错。考虑有两个线程和两个对象的情形,如下图所示。线程1获取object1上的锁,线程2获取object2上的锁,现在线程1等待object2上的锁,线程2等待object1上的锁。每个线程都在等待另一个线程释放它所需要的锁,导致两个线程都无法继续运行(死锁)。

死锁产生的四个必要条件

互斥条件:资源一次只能由一个线程占用

请求与保持条件:线程持有至少一个资源,并等待获取其它线程持有的资源

不剥夺条件:线程已获得的资源在未使用完之前不能被其它线程强行剥夺

循环等待条件:多个线程形成一种头尾相接的循环等待资源关系

示例:账户转账情形

package edu.cuit.avatar.concurrent;

/**

* @author Adan

* @version 1.0

* @date 2025/7/17 10:42

*/

public class BankDeadLockDemo {

private static class Account{

private String name;

private int balance;

public Account(String name, int balance) {

this.name = name;

this.balance = balance;

}

public int getBalance() {

return balance;

}

public String getName() {

return name;

}

public void deposit(int amount){

balance += amount;

}

public void withdraw(int amount){

balance -= amount;

}

@Override

public String toString() {

return name + ':' + balance;

}

}

public static void transfer(Account from, Account to, int amount){

synchronized (from){

System.out.println(Thread.currentThread().getName() + " acquired lock on " + from);

try {

Thread.sleep(100);

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

synchronized (to){

System.out.println(Thread.currentThread().getName() + " acquired lock on " + to);

if(from.balance >= amount){

from.withdraw(amount);

to.deposit(amount);

System.out.println("transfer successful!");

} else

System.out.println("insufficient balance");

}

}

}

public static void main(String[] args) {

Account peppa = new Account("Peppa", 1000);

Account jorge = new Account("jorge", 1000);

new Thread(()-> transfer(peppa, jorge, 500)).start();

new Thread(()-> transfer(jorge, peppa, 500)).start();

}

}

如何避免死锁

使用一种称为资源排序的简单技术可以轻易地避免死锁的发生。该技术是给每一个需要锁的对象指定一个顺序,确保每个线程都按照这个顺序来获取锁。例如,在上图中,假设按照object1,object2的顺序对两个对象进行排序。采用资源排序技术,线程2必须先获取object1上的锁,然后才能获取object2上的锁。一旦线程1获取了object1上的锁,线程2必须等待object1上的锁。所以,线程1就能获取object2上的锁,不会再发生死锁的现象。

使用资源排序技术解决上述转账问题

package edu.cuit.avatar.concurrent;

/**

* @author Adan

* @version 1.0

* @date 2025/7/17 10:42

*/

public class BankDeadLockDemo {

private static class Account{

private String name;

private int balance;

public Account(String name, int balance) {

this.name = name;

this.balance = balance;

}

public int getBalance() {

return balance;

}

public String getName() {

return name;

}

public void deposit(int amount){

balance += amount;

}

public void withdraw(int amount){

balance -= amount;

}

@Override

public String toString() {

return name + ':' + balance;

}

}

public static void transfer(Account from, Account to, int amount){ //资源排序:确保所有线程以相同顺序获取锁

Account object1 = from.name.compareTo(to.name) < 0 ? from : to;

Account object2 = object1 == from ? to : from;

synchronized (object1){

System.out.println(Thread.currentThread().getName() + " acquired lock on " + from);

try {

Thread.sleep(100);

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

synchronized (object2){

System.out.println(Thread.currentThread().getName() + " acquired lock on " + to);

if(from.balance >= amount){

from.withdraw(amount);

to.deposit(amount);

System.out.println("transfer successful!");

} else

System.out.println("insufficient balance");

}

}

}

public static void main(String[] args) {

Account peppa = new Account("Peppa", 1000);

Account jorge = new Account("jorge", 1000);

new Thread(()-> transfer(peppa, jorge, 500)).start();

new Thread(()-> transfer(jorge, peppa, 500)).start();

}

}

检测死锁

可以使用JDK工具检测死锁:

运行程序

使用jps命令查找java进程PID

使用jstack 查看线程堆栈信息,进而了解死锁信息