活锁
2021/4/11 18:31:24
本文主要是介绍活锁,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
目录
1、活锁的概念
2、活锁代码示例
2.1 代码示例
2.2 程序运行结果
3、解决活锁的两种方案
3.1 重试时休眠一个随机时间再进行重试
3.2 严格控制获取资源的顺序
1、活锁的概念
概念与定义:是指两个或两个以上的进程(或线程)在执行过程中,因不断地尝试性获取资源而造成的一种无限循环的现象。
2、活锁代码示例
2.1 代码示例
package com.autocoding.lock.livelock; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import lombok.extern.slf4j.Slf4j; /** * * 场景: * <p> t1线程先尝试获取lock1,再尝试获取lock2,如果获取lock2失败,则释放lock1,再进行重试 </p> * <p> t2线程先尝试获取lock2,再尝试获取lock1,如果获取lock1失败,则释放lock2,再进行重试 </p> * 这种场景是会出现活锁现象的 */ @Slf4j public class LiveLockTest { public static void main(String[] args) throws InterruptedException { final Lock lock1 = new ReentrantLock(); final Lock lock2 = new ReentrantLock(); final Thread thread1 = new Thread(new Runnable() { @Override public void run() { final AtomicInteger counter = new AtomicInteger(); while (true) { if (lock1.tryLock()) { try { LiveLockTest.log.info("获取了lock1,准备获取lock2....."); //为了模拟两个线程相互持有对方需要的锁,这里休眠一下 try { TimeUnit.MILLISECONDS.sleep(1); } catch (final InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (lock2.tryLock()) { try { LiveLockTest.log.info("获取了lock2"); LiveLockTest.log.error("经过{}次重试之后,获取了两把锁,业务逻辑处理", counter); break; } finally { lock2.unlock(); } } } finally { lock1.unlock(); } } counter.incrementAndGet(); } } }); thread1.setName("线程1"); final Thread thread2 = new Thread(new Runnable() { @Override public void run() { final AtomicInteger counter = new AtomicInteger(); while (true) { if (lock2.tryLock()) { try { LiveLockTest.log.info("获取了lock2,准备获取lock1....."); //为了模拟两个线程相互持有对方需要的锁,这里休眠一下 try { TimeUnit.MILLISECONDS.sleep(1); } catch (final InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (lock1.tryLock()) { try { LiveLockTest.log.info("获取了lock1"); LiveLockTest.log.error("经过{}次重试之后,获取了两把锁,业务逻辑处理", counter); break; } finally { lock1.unlock(); } } } finally { lock2.unlock(); } } counter.incrementAndGet(); } } }); thread2.setName("线程2"); thread1.start(); thread2.start(); thread1.join(); thread2.join(); } }
2.2 程序运行结果
经过大量的重试时间,最终两个线程都可以获取锁,继续执行,但是大部分情况是,每次重试都是无效的重试,进行自旋,浪费cpu宝贵的执行时间。
2021-04-11 17:07:25.985 [线程1] INFO com.autocoding.lock.livelock.LiveLockTest - 获取了lock1,准备获取lock2..... 2021-04-11 17:07:25.985 [线程2] INFO com.autocoding.lock.livelock.LiveLockTest - 获取了lock2,准备获取lock1..... 2021-04-11 17:07:25.990 [线程2] INFO com.autocoding.lock.livelock.LiveLockTest - 获取了lock2,准备获取lock1..... 2021-04-11 17:07:25.990 [线程1] INFO com.autocoding.lock.livelock.LiveLockTest - 获取了lock1,准备获取lock2..... 2021-04-11 17:07:25.992 [线程1] INFO com.autocoding.lock.livelock.LiveLockTest - 获取了lock1,准备获取lock2..... 2021-04-11 17:07:25.992 [线程2] INFO com.autocoding.lock.livelock.LiveLockTest - 获取了lock2,准备获取lock1..... 2021-04-11 17:07:25.994 [线程2] INFO com.autocoding.lock.livelock.LiveLockTest - 获取了lock2,准备获取lock1..... 2021-04-11 17:07:25.994 [线程1] INFO com.autocoding.lock.livelock.LiveLockTest - 获取了lock1,准备获取lock2..... 2021-04-11 17:07:25.996 [线程2] INFO com.autocoding.lock.livelock.LiveLockTest - 获取了lock2,准备获取lock1..... 2021-04-11 17:07:25.996 [线程1] INFO com.autocoding.lock.livelock.LiveLockTest - 获取了lock1,准备获取lock2..... 2021-04-11 17:07:25.998 [线程1] INFO com.autocoding.lock.livelock.LiveLockTest - 获取了lock2 2021-04-11 17:07:25.998 [线程1] ERROR com.autocoding.lock.livelock.LiveLockTest - 经过4次重试之后,获取了两把锁,业务逻辑处理 2021-04-11 17:07:25.999 [线程2] INFO com.autocoding.lock.livelock.LiveLockTest - 获取了lock2,准备获取lock1..... 2021-04-11 17:07:26.001 [线程2] INFO com.autocoding.lock.livelock.LiveLockTest - 获取了lock1 2021-04-11 17:07:26.001 [线程2] ERROR com.autocoding.lock.livelock.LiveLockTest - 经过25620次重试之后,获取了两把锁,业务逻辑处理
3、解决活锁的两种方案
3.1 重试时休眠一个随机时间再进行重试
package com.autocoding.lock.livelock; import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import lombok.extern.slf4j.Slf4j; /** * * 场景: * <p> t1线程先尝试获取lock1,再尝试获取lock2,如果获取lock2失败,则释放lock1,再进行重试 </p> * <p> t2线程先尝试获取lock2,再尝试获取lock1,如果获取lock1失败,则释放lock2,再进行重试 </p> * 这种场景是会出现活锁现象的 * <p>解决方案1</p> * 加入一些随机因素,再次竞争资源时,重试时间间隔加入一些随机时间 */ @Slf4j public class Solve1LiveLockTest { public static void main(String[] args) throws InterruptedException { final Lock lock1 = new ReentrantLock(); final Lock lock2 = new ReentrantLock(); final Thread thread1 = new Thread(new Runnable() { @Override public void run() { while (true) { if (lock1.tryLock()) { try { Solve1LiveLockTest.log.info("获取了lock1,准备获取lock2....."); //为了模拟两个线程相互持有对方需要的锁,这里休眠一下 try { TimeUnit.MILLISECONDS.sleep(1); } catch (final InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (lock2.tryLock()) { try { Solve1LiveLockTest.log.info("获取了lock2"); Solve1LiveLockTest.log.info("业务逻辑处理"); break; } finally { lock2.unlock(); } } } finally { lock1.unlock(); } } // 加入一些随机因素,再次竞争资源时,重试时间间隔加入一些随机时间 try { TimeUnit.MILLISECONDS.sleep(new Random().nextInt(10)); } catch (final InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }); thread1.setName("线程1"); final Thread thread2 = new Thread(new Runnable() { @Override public void run() { while (true) { if (lock2.tryLock()) { try { Solve1LiveLockTest.log.info("获取了lock2,准备获取lock1....."); //为了模拟两个线程相互持有对方需要的锁,这里休眠一下 try { TimeUnit.MILLISECONDS.sleep(1); } catch (final InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (lock1.tryLock()) { try { Solve1LiveLockTest.log.info("获取了lock1"); Solve1LiveLockTest.log.info("业务逻辑处理"); break; } finally { lock1.unlock(); } } } finally { lock2.unlock(); } } // 加入一些随机因素,再次竞争资源时,重试时间间隔加入一些随机时间 try { TimeUnit.MILLISECONDS.sleep(new Random().nextInt(10)); } catch (final InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }); thread2.setName("线程2"); thread1.start(); thread2.start(); thread1.join(); thread2.join(); } }
程序运行结果:
2021-04-11 17:09:10.895 [线程1] INFO com.autocoding.lock.livelock.Solve1LiveLockTest - 获取了lock1,准备获取lock2..... 2021-04-11 17:09:10.895 [线程2] INFO com.autocoding.lock.livelock.Solve1LiveLockTest - 获取了lock2,准备获取lock1..... 2021-04-11 17:09:10.899 [线程1] INFO com.autocoding.lock.livelock.Solve1LiveLockTest - 获取了lock2 2021-04-11 17:09:10.899 [线程1] INFO com.autocoding.lock.livelock.Solve1LiveLockTest - 业务逻辑处理 2021-04-11 17:09:10.909 [线程2] INFO com.autocoding.lock.livelock.Solve1LiveLockTest - 获取了lock2,准备获取lock1..... 2021-04-11 17:09:10.911 [线程2] INFO com.autocoding.lock.livelock.Solve1LiveLockTest - 获取了lock1 2021-04-11 17:09:10.911 [线程2] INFO com.autocoding.lock.livelock.Solve1LiveLockTest - 业务逻辑处理
3.2 严格控制获取资源的顺序
package com.autocoding.lock.livelock; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import lombok.extern.slf4j.Slf4j; /** * * 场景: * <p> t1线程先尝试获取lock1,再尝试获取lock2,如果获取lock2失败,则释放lock1,再进行重试 </p> * <p> t2线程先尝试获取lock2,再尝试获取lock1,如果获取lock1失败,则释放lock2,再进行重试 </p> * 这种场景是会出现活锁现象的 * <p>解决方案2</p> * 严格控制获取资源的顺序,所有的线程,都是先获取lock1,再获取lock2 */ @Slf4j public class Solve2LiveLockTest { public static void main(String[] args) throws InterruptedException { final Lock lock1 = new ReentrantLock(); final Lock lock2 = new ReentrantLock(); final Thread thread1 = new Thread(new Runnable() { @Override public void run() { while (true) { if (lock1.tryLock()) { try { Solve2LiveLockTest.log.info("获取了lock1,准备获取lock2....."); //为了模拟两个线程相互持有对方需要的锁,这里休眠一下 try { TimeUnit.MILLISECONDS.sleep(1); } catch (final InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (lock2.tryLock()) { try { Solve2LiveLockTest.log.info("获取了lock2"); Solve2LiveLockTest.log.info("业务逻辑处理"); break; } finally { lock2.unlock(); } } } finally { lock1.unlock(); } } } } }); thread1.setName("线程1"); final Thread thread2 = new Thread(new Runnable() { @Override public void run() { while (true) { if (lock1.tryLock()) { try { Solve2LiveLockTest.log.info("获取了lock1,准备获取lock2....."); //为了模拟两个线程相互持有对方需要的锁,这里休眠一下 try { TimeUnit.MILLISECONDS.sleep(1); } catch (final InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (lock2.tryLock()) { try { Solve2LiveLockTest.log.info("获取了lock2"); Solve2LiveLockTest.log.info("业务逻辑处理"); break; } finally { lock2.unlock(); } } } finally { lock1.unlock(); } } } } }); thread2.setName("线程2"); thread1.start(); thread2.start(); thread1.join(); thread2.join(); } }
程序运行结果:
2021-04-11 17:09:43.755 [线程2] INFO com.autocoding.lock.livelock.Solve2LiveLockTest - 获取了lock1,准备获取lock2..... 2021-04-11 17:09:43.759 [线程2] INFO com.autocoding.lock.livelock.Solve2LiveLockTest - 获取了lock2 2021-04-11 17:09:43.759 [线程2] INFO com.autocoding.lock.livelock.Solve2LiveLockTest - 业务逻辑处理 2021-04-11 17:09:43.759 [线程1] INFO com.autocoding.lock.livelock.Solve2LiveLockTest - 获取了lock1,准备获取lock2..... 2021-04-11 17:09:43.761 [线程1] INFO com.autocoding.lock.livelock.Solve2LiveLockTest - 获取了lock2 2021-04-11 17:09:43.761 [线程1] INFO com.autocoding.lock.livelock.Solve2LiveLockTest - 业务逻辑处理
这篇关于活锁的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-19永别了,微服务架构!
- 2024-05-15鸿蒙生态设备数量超8亿台
- 2024-05-13TiDB + ES:转转业财系统亿级数据存储优化实践
- 2024-05-09“2024鸿蒙零基础快速实战-仿抖音App开发(ArkTS版)”实战课程已上线
- 2024-05-09聊聊如何通过arthas-tunnel-server来远程管理所有需要arthas监控的应用
- 2024-05-09log4j2这么配就对了
- 2024-05-09nginx修改Content-Type
- 2024-05-09Redis多数据源,看这篇就够了
- 2024-05-09Google Chrome驱动程序 124.0.6367.62(正式版本)去哪下载?
- 2024-05-09有没有大佬知道这种数据应该怎么抓取呀?