目录
背景:
示例代码:
类定义与变量声明:
run方法:
main方法 :
死锁示例解释:
总结:
背景:
在Java多线程编程中,死锁(Deadlock)是一个必须警惕和避免的问题。当两个或更多的线程在相互等待对方释放资源时,就会陷入死锁状态。这通常发生在多个线程试图同时访问共享资源,并且每个线程都持有一个资源且等待获取另一个线程持有的资源时。死锁会导致程序无法继续执行,甚至可能导致系统崩溃。
示例代码:
类定义与变量声明:
public class TestDeadLock implements Runnable { public int flag = 1; static Object o1 = new Object(), o2 = new Object();
这里定义一个名为TestDeadLock的类,该类实现了Runnable接口,这意味着它可以作为线程的任务执行,类中有两个静态对象o1和o2,它们将作为同步锁来使用。还有一个非静态变量flag,用于控制线程执行那个逻辑块。
run方法:
public void run() { System.out.println("flag=" + flag); if (flag == 1) { synchronized (o1) { try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } synchronized (o2) { System.out.println("1"); } } } if (flag == 0) { synchronized (o2) { try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } synchronized (o1) { System.out.println("0"); } } }
}
在run方法中,首先打印当前线程的flag值,然后,根据flag的值,线程会尝试获取不同的锁并执行相应的代码
·当flag为1时,线程首选获取o1锁,休眠500毫秒,然后尝试获取o2的锁,如果成功过,则打印"1".
·当flag为2时,线程首选获取o2锁,休眠500毫秒,然后尝试获取o1的锁,如果成功过,则打印"0".
main方法 :
public static void main(String[] args) { TestDeadLock td1 = new TestDeadLock(); TestDeadLock td2 = new TestDeadLock(); td1.flag = 1; td2.flag = 0; Thread t1 = new Thread(td1); t1.start(); // 启动线程t1 Thread t2 = new Thread(td2); t2.start(); // 启动线程t2
}
在min方法中,创建了两个TestDeadLock实例td1和td2,并分别设置它们的flag值为1和0;,然后,基于这两个实例创建两个线程t1和t2,并启动它们
死锁示例解释:
由于td1和flag为1,它首先获取了o1的锁,并休眠500毫秒。在td1休眠期间,td2开始执行并获取了o2的锁。当td1醒来兵尝试获取o2的锁时,它会被阻塞,因为o2的锁已经被td2持有,同样地,当td2醒来兵尝试获取o1的锁时,因为o1的锁已经被td1持有,这样,两个线程都陷入了等待状态,形成了死锁
总结:
为了避免死锁,可以采取以下策略:
策略 | 描述 |
---|---|
保持一致的加锁顺序 | 确保所有线程在访问多个共享资源时都以相同的顺序请求锁。这有助于防止循环等待条件,从而减少死锁的风险。 |
避免嵌套锁 | 尽量减少在同一线程中持有多个锁的时间,并避免在持有锁时请求其他锁。嵌套锁可能增加死锁的风险,因为它增加了线程间相互等待的可能性。 |
使用锁超时 | 为锁的获取设置超时时间。这允许线程在无法及时获取锁时放弃等待,并继续执行其他任务。这有助于避免无限期地等待锁,从而减少了死锁的风险。 |
使用并发工具 | 利用Java提供的并发工具,如Semaphore 、CountDownLatch 、CyclicBarrier 等。这些工具提供了更高级的同步机制,可以帮助简化并发编程并减少死锁的风险。它们通常提供了更灵活的锁定和等待策略,以及更强大的并发控制功能。 |
检测和诊断 | 使用JVM提供的诊断工具(如jstack )来检测和分析死锁。当发现死锁时,分析线程堆栈和锁定关系以找到问题的根源。这些工具可以帮助你快速定位死锁的位置,并提供有关线程状态和锁定情况的信息,以便你能够采取适当的措施来解决问题。 |
代码审查和测试 | 通过代码审查和并发测试来确保代码中没有潜在的死锁风险。在代码审查中,特别关注多线程访问共享资源的部分,确保加锁顺序一致、避免嵌套锁等。在并发测试中,模拟多个线程同时访问共享资源的场景,以验证代码在并发环境下的稳定性和性能,并检查是否存在死锁问题。 |