本文共 2059 字,大约阅读时间需要 6 分钟。
什么是多线程死锁?
多线程以及多进程改善了系统资源的利用率并提高了系统的处理能力。然而,并发执行也带来了新的问题——死锁。死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些线程都将无法向前推进。
死锁主要由两个因素引起:系统资源的竞争和进程推进顺序的不合理。
系统资源的竞争是导致死锁的主要原因之一。许多现代系统都依赖有限的不可剥夺资源(如CPU时间片、磁盘I/O等)来提供服务。这些资源无法同时满足多个进程的需求。如果各进程试图在同一时间请求同一资源,就可能导致死锁。此外,线程间直接调用同步块(如synchronized关键字)也可能引发死锁。
进程推进顺序不合理是另一个导致死锁的主要原因。在并发环境下,进程通常交替地发起和释放资源请求。假设两个进程P1和P2分别持有资源R1和R2,而P1试图请求R2,P2试图请求R1,就有可能出现僵局,导致两者无法继续执行。
以下代码示例展示了一种可能导致死锁的情形:
package com.yxl.demo.ThreadTest;public class Test5 { public static void main(String[] args) throws InterruptedException { TestDemo thread = new TestDemo(); Thread t1 = new Thread(thread, "窗口一"); Thread t2 = new Thread(thread, "窗口二"); t1.start(); Thread.sleep(40); thread.flag = false; t2.start(); }}class TestDemo implements Runnable { private volatile int count = 100; private Object object = new Object(); boolean flag = true; @Override public void run() { if (flag) { while (count > 0) { synchronized (object) { sale(); } } } else { while (count > 0) { sale(); } } } public synchronized void sale() { try { Thread.sleep(50); } catch (Exception e) { } synchronized (object) { if (count > 0) { System.out.println(Thread.currentThread().getName() + "出售:" + (100 - count + 1)); count--; } } }}
在默认情况下,这段代码会导致死锁。运行结果显示,主线程无法正常退出,加锁操作持续存在,导致程序卡顿。
在这个案例中,两个线程都试图在同一锁object
上同步操作。线程t1首先获取锁并执行sale(),t2在t1启动后的40ms后启动,并试图获取同一个锁。由于t1仍在占用锁,t2只能等待。此时,若t1释放了锁,t2可能继续执行。然而,原代码的其他逻辑(如flag的设置)导致资源长时间占用,导致死锁发生。
如何解决死锁问题?
解决死锁问题可以采取以下措施:
加锁顺序(Lock Ordering):确保线程在对多个锁加锁时,按照特定顺序操作,避免竞争。
加锁时限(锁定超时):在尝试获取锁时设置超时,超过时间限制后放弃锁请求并释放已经占有的锁。这有助于减少死锁发生的概率。
防止死锁检测(Deadlock Detection):采用他urka检测机制,监测是否存在长时间占锁状态,以便及时处理。现代一些的环境中,这种方法变得越来越常见。
此外,针对上述代码,可以通过调整运行逻辑来避免死锁问题。例如,允许线程在获取锁前先检查资源是否可用。这样可以避免多线程在同一个锁上进行长时间等待,减少死锁风险。
转载地址:http://dwwaz.baihongyu.com/