线程死锁
什么是线程死锁?
线程死锁描述的是这样一种情况:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无期限的阻塞,因此程序不可能正常终止
如下图所示,线程A 持有 资源2,线程B 持有 资源1,它们同时都想申请对方的资源,但都拿不到,所以这两个线程就会互相等待而进入死锁状态
死锁模拟
/**
* 模拟线程死锁
*/
public class DeadLockMain {
public static void main(String[] args) {
DeadLock t1 = new DeadLock();
DeadLock t2 = new DeadLock();
t1.setFlag(true);
t1.setName("A");
t2.setFlag(false);
t2.setName("B");
t1.start();
t2.start();
}
}
class DeadLock extends Thread{
static Object o1 = new Object();
static Object o2 = new Object();
private boolean flag;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
// 死锁原因
// 1. 如果 flag 为 true,线程A 就会先持有 o1 对象锁,然后尝试去获取 o2 对象锁
// 2. 如果 线程A 得不到 o2 对象锁,就会 Blocked
// 3. 如果 flag 为 false,线程B 就会先持有 o2 对象锁,然后尝试去获取 o1 对象锁
// 4. 如果 线程B 得不到 o1 对象锁,就会 Blocked
// 线程A 得不到 o2 对象锁,就不会释放 o1 对象锁
// 线程B 得不到 o1 对象锁,就不会释放 o2 对象锁
if(flag){
synchronized (o1){
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 1...");
synchronized (o2) {
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 2...");
}
}
}else{
synchronized (o2){
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 3...");
synchronized (o1){
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 4...");
}
}
}
}
}
死锁的必要条件
要产生死锁,必须 同时 具备四个条件
-
互斥条件:当前资源任意一个时刻只能由一个线程占用
-
请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放
-
不剥夺条件:当前线程已获得的资源在未使用完之前不能被其它线程强行剥夺,只有自己使用完毕后才释放资源
-
循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系
避免死锁
既然知道了死锁产生的必要条件,那么只要破坏掉其中任何一个条件,就能避免死锁
1、互斥条件
这个条件无法破坏,因为用 锁 本身就是想让线程之间进行互斥的(临界资源需要互斥访问)
2、请求与保持条件
一个线程因请求资源而阻塞时,对已获得的资源保持不放。破坏的方式是,让当前线程主动释放已获得的资源:
wait()
/**
* 破坏死锁的必要条件-请求与保持条件
*/
public class DestroyDeadLock01Main {
public static void main(String[] args) {
DestroyDeadLock01 t1 = new DestroyDeadLock01();
DestroyDeadLock01 t2 = new DestroyDeadLock01();
t1.setFlag(true);
t1.setName("A");
t2.setFlag(false);
t2.setName("B");
t1.start();
t2.start();
}
}
class DestroyDeadLock01 extends Thread{
static Object o1 = new Object();
static Object o2 = new Object();
private boolean flag;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag){
synchronized (o1){
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 1...");
try {
// 主动释放对 o1 的持有
// 等其它线程使用完 o1 后,再来唤醒当前线程
o1.wait(); // wait() 方法需要其它线程手动 notify() 唤醒
// o1.wait(50); // wait(50) 等待 50 毫秒后,自己唤醒自己,不需要其它线程通过 notify() 手动唤醒
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 2...");
}
}
}else{
synchronized (o2){
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 3...");
synchronized (o1){
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 4...");
// 对 o1 使用完毕后,唤醒其它线程来争夺当前的 o1 锁
o1.notify();
}
}
}
}
}
3、不剥夺条件
当前线程已获得的资源在未使用完之前不能被其它线程强行剥夺,只有自己使用完毕后才释放资源。破坏的方式是,先让当前线程将获得的资源使用完,其它线程再来拿该资源
/**
* 破坏死锁的必要条件-不剥夺条件
*/
public class DestroyDeadLock02Main {
public static void main(String[] args) {
DestroyDeadLock02 t1 = new DestroyDeadLock02();
DestroyDeadLock02 t2 = new DestroyDeadLock02();
t1.setFlag(true);
t1.setName("A");
t2.setFlag(false);
t2.setName("B");
t1.start();
t2.start();
}
}
class DestroyDeadLock02 extends Thread{
static Object o1 = new Object();
static Object o2 = new Object();
private boolean flag;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag){
synchronized (o1){
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 1...");
synchronized (o2) {
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 2...");
}
}
}else{
try {
// 让 B线程 先休眠 50 毫秒
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 此时 A线程 已经完成了对资源的使用,并且释放了 锁
// 再让 B线程 去拿锁
synchronized (o2){
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 3...");
synchronized (o1){
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 4...");
}
}
}
}
}
4、循环等待条件
若干线程之间形成一种头尾相接的循环等待资源关系。破坏的方式是:按序申请资源,不让若干线程形成头尾相接的循环等待资源关系
/**
* 破坏死锁的必要条件-循环等待条件
*/
public class DestroyDeadLock03Main {
public static void main(String[] args) {
DestroyDeadLock03 t1 = new DestroyDeadLock03();
DestroyDeadLock03 t2 = new DestroyDeadLock03();
t1.setFlag(true);
t1.setName("A");
t2.setFlag(false);
t2.setName("B");
t1.start();
t2.start();
}
}
class DestroyDeadLock03 extends Thread{
static Object o1 = new Object();
static Object o2 = new Object();
private boolean flag;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag){
// 线程A 先拿到 o1 的锁,线程A 执行完毕,释放 o1
// 线程B 拿不到 o1 的锁,进入阻塞(Blocked)状态,等待 线程A 释放 o1
synchronized (o1){
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 1...");
synchronized (o2) {
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 2...");
}
}
}else{
// 线程B 先拿到 o1 的锁,线程B 执行完毕,释放 o1
// 线程A 拿不到 o1 的锁,进入阻塞(Blocked)状态,等待 线程B 释放 o1
synchronized (o1){
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 3...");
synchronized (o2){
System.out.println("线程 " + Thread.currentThread().getName() + " 进入 4...");
}
}
}
}
}