死锁

死锁是说两个或多个线程等待着一个线程释放资源而无法执行,而等待的线程又不释放资源导致线程阻塞。使得任务无法执行

发生死锁的必要条件(四个必要条件同时满足才会导致死锁):

  • 互斥(Mutual Exclusion):某些资源只能由一个线程独占使用。

  • 持有并等待(Hold and Wait):线程持有某些资源的同时,还在等待其他资源。

  • 不可剥夺(No Preemption):已分配的资源不能被强制剥夺,必须由持有者主动释放。

  • 循环等待(Circular Wait):存在一个线程集合,其中每个线程都在等待另一个线程持有的资源,形成一个闭环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Lock {  
static final Object resource1 = new Object();
static final Object resource2 = new Object();

public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Locked resource1");
try { Thread.sleep(100); } catch (InterruptedException ignored) {}
synchronized (resource2) {
System.out.println("Thread 1: Locked resource2");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("Thread 2: Locked resource2");
try {
Thread.sleep(100);
} catch (InterruptedException ignored) {}
synchronized (resource1) {
System.out.println("Thread 2: Locked resource1");
}
}
});
t1.start();
t2.start();
}
}

第一个锁锁了自己,想要访问第二个锁,但是第二个锁锁了自己,无法被访问到,同理对于第二个锁也无法访问第一个锁。

解决办法shi

  • 避免循环等待:规定资源获取的顺序,所有线程按照相同的顺序获取资源。使得线程1始终先于线程二执行(同时锁第一个,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    class Lock {  
    static final Object resource1 = new Object();
    static final Object resource2 = new Object();

    public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
    synchronized (resource1) {
    System.out.println("Thread 1: Locked resource1");
    try { Thread.sleep(100); } catch (InterruptedException ignored) {}
    synchronized (resource2) {
    System.out.println("Thread 1: Locked resource2");
    }
    }
    });
    Thread t2 = new Thread(() -> {
    synchronized (resource1) { // 这里改成先锁 resource1,确保顺序一致
    System.out.println("Thread 2: Locked resource1");
    try { Thread.sleep(100); } catch (InterruptedException ignored) {}
    synchronized (resource2) {
    System.out.println("Thread 2: Locked resource2");
    }
    }
    });
    t1.start();
    t2.start();
    }
    }
  • 使用超时机制:如 tryLock() 方式获取锁,超时则放弃并重试。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    import java.util.concurrent.locks.ReentrantLock;

    class TryLockSolution {
    static final ReentrantLock lock1 = new ReentrantLock();
    static final ReentrantLock lock2 = new ReentrantLock();

    public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
    while (true) {
    if (lock1.tryLock()) {
    try {
    System.out.println("Thread 1: Locked lock1");
    try {
    Thread.sleep(50);
    } catch (InterruptedException ignored) {}

    if (lock2.tryLock()) {
    try {
    System.out.println("Thread 1: Locked lock2");
    break;
    } finally {
    lock2.unlock();
    }
    }
    } finally {
    lock1.unlock();
    }
    }
    try {
    Thread.sleep(50);
    } catch (InterruptedException ignored) {}
    }
    });

    Thread t2 = new Thread(() -> {
    while (true) {
    if (lock2.tryLock()) {
    try {
    System.out.println("Thread 2: Locked lock2");
    try {
    Thread.sleep(50);
    } catch (InterruptedException ignored) {}

    if (lock1.tryLock()) {
    try {
    System.out.println("Thread 2: Locked lock1");
    break;
    } finally {
    lock1.unlock();
    }
    }
    } finally {
    lock2.unlock();
    }
    }
    try {
    Thread.sleep(50);
    } catch (InterruptedException ignored) {}
    }
    });

    t1.start();
    t2.start();
    }
    }

  • 使用Lock + Condition机制:当你希望先获取到两个资源再执行任务时候,可以使用这个方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    import java.util.concurrent.locks.ReentrantLock;

    class LockConditionSolution {
    static final ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
    lock.lock();
    try {
    System.out.println("Thread 1: Acquired lock, performing operations...");
    try { Thread.sleep(100); } catch (InterruptedException ignored) {}
    } finally {
    lock.unlock();
    }
    });

    Thread t2 = new Thread(() -> {
    lock.lock();
    try {
    System.out.println("Thread 2: Acquired lock, performing operations...");
    try { Thread.sleep(100); } catch (InterruptedException ignored) {}
    } finally {
    lock.unlock();
    }
    });

    t1.start();
    t2.start();
    }
    }

    lock.lock() 确保只有一个线程能进入临界区,所以不会有两个线程同时获取不同的锁,从而避免了死锁

    • 使用Timeout机制
      获取锁失败就释放已有的锁并退出,避免无限等待
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      import java.util.concurrent.locks.ReentrantLock;
      import java.util.concurrent.TimeUnit;

      class TimeoutSolution {
      static final ReentrantLock lock1 = new ReentrantLock();
      static final ReentrantLock lock2 = new ReentrantLock();

      public static void main(String[] args) {
      Thread t1 = new Thread(() -> {
      try {
      if (lock1.tryLock(500, TimeUnit.MILLISECONDS)) {
      try {
      System.out.println("Thread 1: Locked lock1");
      Thread.sleep(100);
      if (lock2.tryLock(500, TimeUnit.MILLISECONDS)) {
      try {
      System.out.println("Thread 1: Locked lock2");
      } finally {
      lock2.unlock();
      }
      } else {
      System.out.println("Thread 1: Couldn't lock lock2, releasing lock1");
      }
      } finally {
      lock1.unlock();
      }
      } else {
      System.out.println("Thread 1: Couldn't lock lock1");
      }
      } catch (InterruptedException ignored) {}
      });

      Thread t2 = new Thread(() -> {
      try {
      if (lock2.tryLock(500, TimeUnit.MILLISECONDS)) {
      try {
      System.out.println("Thread 2: Locked lock2");
      Thread.sleep(100);
      if (lock1.tryLock(500, TimeUnit.MILLISECONDS)) {
      try {
      System.out.println("Thread 2: Locked lock1");
      } finally {
      lock1.unlock();
      }
      } else {
      System.out.println("Thread 2: Couldn't lock lock1, releasing lock2");
      }
      } finally {
      lock2.unlock();
      }
      } else {
      System.out.println("Thread 2: Couldn't lock lock2");
      }
      } catch (InterruptedException ignored) {}
      });

      t1.start();
      t2.start();
      }
      }

活锁

两个线程间相互让步导致没能完成任务,这种情况下线程并没有阻塞,但是一直在做没有用的循环导致没有成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.util.concurrent.locks.ReentrantLock;  

class Lock {
static final ReentrantLock lock1 = new ReentrantLock();
static final ReentrantLock lock2 = new ReentrantLock();

public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
if (lock1.tryLock()) {
System.out.println("Thread 1: Locked lock1, waiting for lock2...");
try { Thread.sleep(50); } catch (InterruptedException ignored) {}

if (!lock2.tryLock()) { // 如果无法获取 lock2,则释放 lock1 并重试
System.out.println("Thread 1: Couldn't get lock2, releasing lock1...");
lock1.unlock();
try { Thread.sleep(10); } catch (InterruptedException ignored) {}
} else {
System.out.println("Thread 1: Acquired both locks!");
lock2.unlock();
lock1.unlock();
return;
}
}
}
});
Thread t2 = new Thread(() -> {
while (true) {
if (lock2.tryLock()) {
System.out.println("Thread 2: Locked lock2, waiting for lock1...");
try { Thread.sleep(50); } catch (InterruptedException ignored) {}

if (!lock1.tryLock()) { // 如果无法获取 lock1,则释放 lock2 并重试
System.out.println("Thread 2: Couldn't get lock1, releasing lock2...");
lock2.unlock();
try { Thread.sleep(10); } catch (InterruptedException ignored) {}
} else {
System.out.println("Thread 2: Acquired both locks!");
lock1.unlock();
lock2.unlock();
return;
}
}
}
});
t1.start();
t2.start();
}}

线程一获得锁1后尝试获得锁二来执行任务,线程二获得锁二后尝试获得线程一来执行任务,但是线程一无法拿到线程二的锁,于是释放资源后重新开始,线程二也在做一样的事,两个线程相互让导致活锁死循环。

解决方案:

  • 引入随机等待时间,避免两个线程总是同时尝试相同的操作。

  • 使用更高级的锁机制,如 ReentrantLocktryLock(time, unit),超时后放弃尝试