活锁与死锁

简介: 【8月更文挑战第22天】

定义

  • 活锁:一种并发编程中的现象,其中两个或多个线程相互等待对方释放资源,但由于某种原因,没有任何线程能够继续执行,从而导致所有涉及的线程都陷入无限循环。
  • 死锁:一种并发编程中的现象,其中两个或多个线程无限期地等待对方释放锁定的资源,导致所有涉及的线程都无法继续执行。

区别

活锁和死锁虽然都是并发编程中的问题,但两者之间存在一些关键区别:

  • 资源获取:在死锁中,线程已经获取了所需的资源并被锁住,而活锁中线程尚未获取资源。
  • 线程状态:在死锁中,线程处于 BLOCKED 状态,等待其他线程释放锁定的资源。在活锁中,线程处于 RUNNABLE 状态,但由于某种原因无法继续执行。
  • 检测方法:死锁可以通过检测循环等待的线程来检测,而活锁更难检测,因为它涉及线程之间的间接依赖关系。

活锁的示例

考虑以下代码示例:

public class LiveLockExample {
   
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    public static void main(String[] args) {
   
        Thread thread1 = new Thread(() -> {
   
            synchronized (lock1) {
   
                try {
   
                    Thread.sleep(100); // 模拟耗时操作
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }

                synchronized (lock2) {
   
                    // ...
                }
            }
        });

        Thread thread2 = new Thread(() -> {
   
            synchronized (lock2) {
   
                try {
   
                    Thread.sleep(100); // 模拟耗时操作
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }

                synchronized (lock1) {
   
                    // ...
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

在这个示例中,两个线程相互等待对方释放锁定的对象,导致活锁。线程 1 等待线程 2 释放 lock2,而线程 2 等待线程 1 释放 lock1。由于两个线程都在不断尝试获取对方持有的锁,它们陷入了一个无限循环,无法继续执行。

死锁的示例

考虑以下代码示例:

public class DeadlockExample {
   
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    public static void main(String[] args) {
   
        Thread thread1 = new Thread(() -> {
   
            synchronized (lock1) {
   
                try {
   
                    Thread.sleep(100); // 模拟耗时操作
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }

                synchronized (lock2) {
   
                    // ...
                }
            }
        });

        Thread thread2 = new Thread(() -> {
   
            synchronized (lock2) {
   
                try {
   
                    Thread.sleep(100); // 模拟耗时操作
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }

                synchronized (lock1) {
   
                    // ...
                }
            }
        });

        thread1.start();
        thread2.start();

        // 等待线程完成
        try {
   
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
    }
}

在这个示例中,两个线程都试图获取两个锁 (lock1lock2),但由于线程 1 先获取了 lock1,而线程 2 先获取了 lock2,它们都无法继续执行并等待对方释放锁。这导致了死锁。

如何避免

避免活锁和死锁的方法包括:

  • 避免嵌套锁
  • 始终以相同的顺序获取锁
  • 使用 Lock 对象和 tryLock() 方法
  • 使用非阻塞数据结构
  • 避免资源饥饿

结论

活锁和死锁都是并发编程中常见的陷阱。理解它们的差异并采取适当的预防措施对于编写健壮且无错误的多线程应用程序至关重要。

目录
相关文章
|
3月前
死锁原因
死锁原因
40 1
|
6月前
|
程序员
|
6月前
|
SQL 存储 设计模式
如何与死锁斗争!!!
尽量不要改动线上数据库的字段,因为会触发锁表影响业务,严重时还可能出现死锁!数据库真的出现了死锁,业务全挂了,这种时候应该怎么办呢?本文就给大家分享一下数据库死锁的排查思路,万一出了问题,也有底气去解决。
62 1
|
6月前
|
安全 Java 测试技术
发生死锁怎么办
发生死锁怎么办
66 0
|
6月前
死锁的发生与避免
死锁的发生与避免 死锁是指两个或者多个进程在执行过程中,因争夺资源而造成的一种僵局,若无外力作用,它们都将无法推进下去。在计算机系统中,死锁是一种常见的问题,因此需要采取一些措施来避免死锁的发生。
|
安全 算法
死锁的总结(1)
死锁的总结
33 0
|
算法 安全
死锁的总结(2)
死锁的总结
47 0
|
算法 调度
死锁的理解
死锁的理解
83 0
|
安全 算法
初识死锁问题
关于对死锁的详解