死锁简介:
定义:多个线程同时等待其他线程释放锁,导致被无限期阻塞
原因:
- A线程持有锁1,这时主内存的锁1变量进入锁定状态,其他想获得此变量的的线程必须等待。B线程持有锁2,主内存中的锁2变量进入锁定状态。
- 这时A线程再去获取锁2,B线程再去获取锁1,而此时A、B线程都没有对原先锁变量进行解锁,故A线程等待B线程释放锁2,而B线程等待A线程释放锁1。
- 这时就出现了A、B线程同时被无限期阻塞,故导致死锁
代码实现:
package com.yitian.lock;
/**
* @Description TODO 实现死锁
* @Author yitianRen
* @Date 2019/7/25 15:18
* @Version 1.0
**/
public class DeadLock {
protected static String locak1="1";
protected static String locak2="2";
public static void main(String[] args) {
Thread thread1=new Thread(new lock1());
Thread thread2=new Thread(new lock2());
thread1.start();
thread2.start();
}
}
class lock1 implements Runnable{
@Override
public void run() {
synchronized (DeadLock.locak1){
System.out.println("lock1:我获得了第一个?");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (DeadLock.locak2){
System.out.println("lock1:我获得了第二个?");
}
}
}
}
class lock2 implements Runnable{
@Override
public void run() {
synchronized (DeadLock.locak2){
System.out.println("lock2:我获得了第一个?");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (DeadLock.locak1){
System.out.println("lock2:我获得了第二个?");
}
}
}
}
避免方法:
- 加锁顺序:上述例子出现死锁因为A、B线程加锁的顺序不同,如果按照相同顺序,则可以避免死锁
public static void main(String[] args) {
Thread thread1 = new Thread(new lock1());
Thread thread2 = new Thread(new lock2());
thread1.start();
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
}
- 加锁时限:给锁加一个超时时间,若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得 的锁,然后等待一段随机的时间再重试。( 注:这种机制存在一个问题,在Java中不能对synchronized同步块设置超时时间。你需要创建一个自定义锁,或使用Java5中java.util.concurrent包下的工具)
- 死锁检测:死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。(注: 还是上述例子,线程A持有锁1,请求锁2,线程B持有锁2,请求锁1。这时可以让线程A去检测线程B是否已经请求了线程A当前锁持有的锁,如果线程B的确是在请求,则线程A取消请求,并释放锁1,回退和等待。当然现实可能多条线程交叉,它需要递进地检测。)