概述
虽然多线程有助与提升应用程序性能,同时也引入了一些问题。在本教程里,我们使用Java示例探讨死锁和活锁问题。
什么是死锁?
两个或多个线程彼此一直等待被其他线程持有的一个锁或者资源 此时死锁就会产生。换句话说就是某个时间点线程T1持有一个或多个锁或者资源(lock1, …),为了完成操作它需要获得其他锁(lock2, …),以此同时其他线程T2已经获得了lock2,T2为了完成操作也需要获取lock1。因此,由于死锁线程无法进行,应用程序可能会暂停或失败。
经典的哲学家进餐问题很好的阐述了多线程环境下同步问题,并且经常用作死锁例子。
死锁例子
为了更好的理解死锁,首先,让我们一起来看看一个简单的Java例子。
在这个例子中,我们创建两个线程,T1和T2。线程T1调用operation1方法,线程T2调用operation2方法。
为了完成他们的操作,线程T1需要先获取lock1然后获取lock2,然而线程T2需要先获取lock2再获取lock1。因此,两个线程尝试获取锁的顺序相反。
public class DeadlockExample {
private Lock lock1 = new ReentrantLock(true);
private Lock lock2 = new ReentrantLock(true);
public static void main(String[] args) {
DeadlockExample deadlock = new DeadlockExample();
new Thread(deadlock::operation1, "T1").start();
new Thread(deadlock::operation2, "T2").start();
}
public void operation1() {
lock1.lock();
print("lock1 acquired, waiting to acquire lock2.");
sleep(50);
lock2.lock();
print("lock2 acquired");
print("executing first operation.");
lock2.unlock();
lock1.unlock();
}
public void operation2() {
lock2.lock();
print("lock2 acquired, waiting to acquire lock1.");
sleep(50);
lock1.lock();
print("lock1 acquired");
print("executing second operation.");
lock1.unlock();
lock2.unlock();
}
// helper methods
}
运行代码后可以看到如下输出:
Thread T1: lock1 acquired, waiting to acquire lock2.
Thread T2: lock2 acquired, waiting to acquire lock1.
一旦我们运行这个程序,我们将看见程序产生死锁并且无法结束。日志显示线程T1等待lock2,然而lock2已经被T2持有。类似,线程T2等待lock1,然而lock1已经被线程T1持有。
避免死锁
死锁是Java常见的并发问题。因此,我们设计一个Java程序时需要避免任何潜在的死锁条件。
首先需要避免一个线程获取多个锁。如果一定要获取多个锁,我们需要确保每个线程以相同的顺序获得锁,避免获取锁时循环依赖。
我们也可以尝试使用Lock接口中的超时等待方法获取锁。确保线程在无法获取锁时不会无限阻塞。
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,一年会员只需98元,全站资源免费下载 点击查看详情
站 长 微 信: lzxmw777