• 活跃性问题概述

    • 线程没有按照预期结束或执行不下去的情况,属于活跃性问题
    • 活跃性问题包括死锁、活锁、饥饿三种情况
  • 解决活跃性问题

Java通过使用ReentrantLock解决三种活跃性问题

1 死锁

  • 多把锁概述
    • 有些场景使用一把锁时会导致并发度降低,从而导致性能的下降,考虑以下场景
    • 一间大屋子有两个功能:睡觉、学习,两者互不相干。现在小南要学习,小女要睡觉,但如果只用一间屋子(一个对象锁)的话,那么并发度很低,解决方法是准备多个房间(多个对象锁)
    • 对于没有关联的业务可以给予不同的锁(如睡觉和学习),以增强并发度

但是多把锁的坏处是如果一个线程需要同时获得多把锁,容易发生死锁

  • 死锁概述

    • 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
    • 此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
  • 死锁实例

t1线程获得锁对象A,接下来想获取锁对象B;t2线程获得锁对象B,接下来想获取锁对象A,此时就发生了死锁

  1. Object A = new Object();
  2. Object B = new Object();
  3. Thread t1 = new Thread(() -> {
  4. synchronized (A) {
  5. log.debug("lock A");
  6. try {
  7. Thread.sleep(1000);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. synchronized (B) {
  12. log.debug("lock B");
  13. log.debug("操作...");
  14. }
  15. }
  16. }, "t1");
  17. Thread t2 = new Thread(() -> {
  18. synchronized (B) {
  19. log.debug("lock B");
  20. try {
  21. Thread.sleep(500);
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. synchronized (A) {
  26. log.debug("lock A");
  27. log.debug("操作...");
  28. }
  29. }
  30. }, "t2");
  31. t1.start();
  32. t2.start();
  • 定位/检测死锁
    • 如果发生死锁,首先需要定位死锁,然后才能尝试解决死锁
    • 定位死锁可以使用jconsole工具

2 活锁

  • 活锁概述

    • 活锁指的是任务或者执行者没有被阻塞由于某些条件没有满足,导致一直重复 尝试—失败—尝试—失败的过程。
    • 处于活锁的实体是在不断的改变状态,例如两个线程互相改变对方的结束条件
    • 活锁有可能自行解开
  • 活锁实例 ```java static volatile int count = 10; static final Object lock = new Object();

public static void main(String[] args) { new Thread(() -> { while (count > 0) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } count—; log.debug(“count: {}”, count); } }, “t1”).start(); new Thread(() -> { while (count < 20) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } count++; log.debug(“count: {}”, count); } }, “t2”).start(); } ```

3 饥饿

  • 饥饿概述

饥饿 ,与死锁和活锁非常相似。是指一个可运行的进程尽管能继续执行,但被调度器无限期地忽视,而不能被调度执行的情况