Java线程的死锁和活锁
2021-05-07 10:28
标签:经典 example pre 逻辑 href 相同 常见 cannot 依赖 当多线程帮助我们提高应用性能的同时,它同时也带来一些问题,本文我们将借助几个小例子看下两个问题,死锁和活锁。 死锁发生在当两个或多个线程一直在等待另一个线程持有的锁或资源的时候。这会导致一个程序可能会被拖垮或者直接挂掉,因为线程们都不能继续工作了。 经典的 首先,我们看一个简单的Java例子来理解死锁。 在这个例子中,我们创建两个线程,T1和T2。线程T1调用operation1,线程T2调用operation2。 为了完成操作,线程T1需要先获取到lock1再获取到lock2,然后此时线程T2需要先获取到lock2再获取到lock1。因此两个线程都在以相反的顺序获取锁。 现在,我们写一下DeadlockExample: 我们运行一下这个例子看下输出: 一运行这个例子我们就能看到程序导致了一个死锁且永远也退出不了。输出日志展示了线程T1在等待lock2,但lock2被线程T2所持有。相似的,线程T2在等待lock1,他被T1所持有。 死锁在Java中是个很常见的并发问题,因为我们应该设计一个程序来避免潜在的死锁条件。 活锁是另一个并发问题,它和死锁很相似。在活锁中,两个或多个线程彼此间一直在转移状态,而不像我们上个例子中互相等待。结果就是所有线程都不能执行它们各自的任务。 一个比较好的活锁例子就是消息队列。当发生异常的时候,消息消费者回滚事务并把消息放到队列头中,然后相同的消息又从队列头中被读到,又会造成异常并再次放入到队列头中。如此循坏往复,消费者永远读不到队列中其他的消息。 现在我们展示一下活锁的情况,我们同样拿上面死锁的例子来解释。线程T1调用operation1,线程T2调用operation2,但是我们稍微改变的操作的逻辑。 两个线程都需要拿到两把锁来完成工作,每个线程拿到第一个锁后都会发现拿不到第二把锁,因此为了让另一个线程先完成任务,每个线程都会释放第一把锁并会尝试再次获取到两把锁。 我们来看下下面的测试例子 我们看下运行结果: 能看到输出结果里,两个线程都在重复的获取锁和释放锁,导致两个线程都不能完成操作。 避免活锁我们得观察一下活锁发生的条件并根据情况提出方案,比如: Java线程的死锁和活锁 标签:经典 example pre 逻辑 href 相同 常见 cannot 依赖 原文地址:https://www.cnblogs.com/mrcharleshu/p/13185441.html
1、概览
2、死锁
2.1、什么是死锁
哲学家进餐
问题非常好的展示了多线程下的同步问题并且经常被用来当作死锁的例子。2.2 死锁举例
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.
2.3 避免死锁
避免获取锁时的循环依赖问题
。tryLock
方法,来确保一个线程如果获取不到锁不会一直阻塞。3、活锁
3.1 什么是活锁
3.2 活锁举例
public class LivelockExample {
?
????private Lock lock1 = new ReentrantLock(true);
????private Lock lock2 = new ReentrantLock(true);
?
????public static void main(String[] args) {
????????LivelockExample livelock = new LivelockExample();
????????new Thread(livelock::operation1, "T1").start();
????????new Thread(livelock::operation2, "T2").start();
????}
?
????public void operation1() {
????????while (true) {
????????????tryLock(lock1, 50);
????????????print("lock1 acquired, trying to acquire lock2.");
????????????sleep(50);
?
????????????if (tryLock(lock2)) {
????????????????print("lock2 acquired.");
????????????} else {
????????????????print("cannot acquire lock2, releasing lock1.");
????????????????lock1.unlock();
????????????????continue;
????????????}
?
????????????print("executing first operation.");
????????????break;
????????}
????????lock2.unlock();
????????lock1.unlock();
????}
?
????public void operation2() {
????????while (true) {
????????????tryLock(lock2, 50);
????????????print("lock2 acquired, trying to acquire lock1.");
????????????sleep(50);
?
????????????if (tryLock(lock1)) {
????????????????print("lock1 acquired.");
????????????} else {
????????????????print("cannot acquire lock1, releasing lock2.");
????????????????lock2.unlock();
????????????????continue;
????????????}
?
????????????print("executing second operation.");
????????????break;
????????}
????????lock1.unlock();
????????lock2.unlock();
????}
?
????// helper methods
}
Thread T1: lock1 acquired, trying to acquire lock2.
Thread T2: lock2 acquired, trying to acquire lock1.
Thread T1: cannot acquire lock2, releasing lock1.
Thread T2: cannot acquire lock1, releasing lock2.
Thread T2: lock2 acquired, trying to acquire lock1.
Thread T1: lock1 acquired, trying to acquire lock2.
Thread T1: cannot acquire lock2, releasing lock1.
Thread T1: lock1 acquired, trying to acquire lock2.
Thread T2: cannot acquire lock1, releasing lock2.
3.3 避免活锁
上一篇:GetMidStr c++
下一篇:c++运算符重载