目录
1、活锁的概念
2、活锁代码示例
2.1 代码示例
2.2 程序运行结果
3、解决活锁的两种方案
3.1 重试时休眠一个随机时间再进行重试
3.2 严格控制获取资源的顺序
概念与定义:是指两个或两个以上的进程(或线程)在执行过程中,因不断地尝试性获取资源而造成的一种无限循环的现象。
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(); } }
经过大量的重试时间,最终两个线程都可以获取锁,继续执行,但是大部分情况是,每次重试都是无效的重试,进行自旋,浪费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次重试之后,获取了两把锁,业务逻辑处理
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 - 业务逻辑处理
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 - 业务逻辑处理