时间:2023-01-12 12:39:38 | 栏目:JAVA代码 | 点击:次
活锁同样会发生在多个相互协作的线程间,当他们为了彼此间的响应而相互礼让,使得没有一个线程能够继续前进,那么就发生了活锁。同死锁一样,发生活锁的线程无法继续执行。
相当于两个在半路相遇的人:出于礼貌他们相互礼让,避开对方的路,但是在另一条路上又相遇了。就这样,不停地一直避让下去。。。。
两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候,死锁会让你的程序挂起无法完成任务。
在 Java 中使用多线程,就会有可能导致死锁问题。死锁会让程序一直卡住,不再程序往下执行。我们只能通过中止并重启的方式来让程序重新执行。这是我们非常不愿意看到的一种现象,我们要尽可能避免死锁的情况发生!
指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用完释放。
指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{A,B,C,···,Z} 中的A正在等待一个B占用的资源;B正在等待C占用的资源,……,Z正在等待已被A占用的资源。
/** @author Strive */ @SuppressWarnings("all") public class DeadLock implements Runnable { public int flag = 1; /** 面包 */ private static final Object bread = new Object(); /** 水 */ private static final Object water = new Object(); @Override public void run() { if (flag == 1) { // 先吃面包再喝水,环路等待条件 synchronized (bread) { try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } System.out.println("面包吃了,等喝水。。。"); synchronized (water) { System.out.println("面包吃了,水也喝到了"); } } } if (flag == 0) { // 先喝水再吃面包,环路等待条件 synchronized (water) { try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } System.out.println("喝完水,等吃面包了。。。"); synchronized (bread) { System.out.println("喝完水,面包也吃完了。。。"); } } } } public static void main(String[] args) { DeadLock lockBread = new DeadLock(); DeadLock lockWater = new DeadLock(); lockBread.flag = 1; lockWater.flag = 0; // lockBread,lockWater 都处于可执行状态,但 JVM 线程调度先执行哪个线程是不确定的。 // lockWater 的 run() 可能在 lockBread 的 run() 之前运行 new Thread(lockBread).start(); new Thread(lockWater).start(); } }
程序运行结果:
喝完水,等吃面包了。。。
面包吃了,等喝水。。。
先执行上面的代码,再进行排查!
1、使用 jps -l
D:\workspace-code\gitee\dolphin>jps -l 7072 org.jetbrains.jps.cmdline.Launcher 8824 sun.tools.jps.Jps 17212 4012 com.dolphin.thread.locks.DeadLock 6684 org.jetbrains.idea.maven.server.RemoteMavenServer36
4012 com.dolphin.thread.locks.DeadLock,粘贴进程号 4012
2、使用 jstack 4012
... ... Java stack information for the threads listed above: =================================================== "Thread-1": at com.dolphin.thread.locks.DeadLock.run(DeadLock.java:40) - waiting to lock <0x000000076bf9e3d8> (a java.lang.Object) - locked <0x000000076bf9e3e8> (a java.lang.Object) at java.lang.Thread.run(Thread.java:748) "Thread-0": at com.dolphin.thread.locks.DeadLock.run(DeadLock.java:26) - waiting to lock <0x000000076bf9e3e8> (a java.lang.Object) - locked <0x000000076bf9e3d8> (a java.lang.Object) at java.lang.Thread.run(Thread.java:748) Found 1 deadlock.
从打印信息我们可以看出:
0x000000076bf9e3e8
等待 0x000000076bf9e3d8
锁;0x000000076bf9e3d8
等待 0x000000076bf9e3e8
锁;其实就是破坏死锁的四个必要条件!!!
两个线程试图以不同的顺序来获得相同的锁,如果按照相同的顺序来请求锁,那么就不会出现循环的加锁依赖性,因此也就不会产生死锁,每个需要锁面包和锁水的线程都以相同的顺序来获取面包和水,那么就不会发生死锁了,如下图所示:
根据上图我们修改一下之前写的死锁代码,消除死锁!
@Override public void run() { if (flag == 1) { // 先吃面包再喝水,环路等待条件 synchronized (bread) { try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } System.out.println("面包吃了,等喝水。。。"); } synchronized (water) { System.out.println("面包吃了,水也喝到了"); } } if (flag == 0) { // 先喝水再吃面包,环路等待条件 synchronized (water) { try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } System.out.println("喝完水,等吃面包了。。。"); } synchronized (bread) { System.out.println("喝完水,面包也吃完了。。。"); } } }
程序运行结果:
喝完水,等吃面包了。。。
面包吃了,等喝水。。。
面包吃了,水也喝到了
喝完水,面包也吃完了。。。
这样就可以消除死锁发生~~~
import java.lang.management.ManagementFactory; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; /** @author Strive */ public class LiveLock { static ReentrantLock bread = new ReentrantLock(); static ReentrantLock water = new ReentrantLock(); public static void main(String[] args) { String name = ManagementFactory.getRuntimeMXBean().getName(); String pid = name.split("@")[0]; System.out.println("Pid is:" + pid); System.out.println("jstack " + pid); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.submit( () -> { try { // 尝试获得 bread 锁 while (bread.tryLock(10, TimeUnit.SECONDS)) { System.out.println("拿到面包了,等着拿水。。。"); TimeUnit.SECONDS.sleep(1); // 判断 water 是否被锁住,如果锁住,退出!再次尝试获取 water 锁 boolean locked = water.isLocked(); if (locked) { bread.unlock(); continue; } // 尝试获得 water 锁 boolean w = water.tryLock(10, TimeUnit.SECONDS); // 如果没有获取到 water 锁,释放 bread 锁,再次尝试! if (!w) { bread.unlock(); continue; } System.out.println("拿到水了"); break; } System.out.println("吃面包,喝水!"); } catch (InterruptedException e) { System.out.println("线程中断"); } finally { water.unlock(); bread.unlock(); } }); executorService.submit( () -> { try { while (water.tryLock(10, TimeUnit.SECONDS)) { System.out.println("拿到水了,等着拿面包。。。"); TimeUnit.SECONDS.sleep(2); // 判断 bread 是否被锁住,如果锁住,退出!再次尝试获取 bread 锁 boolean locked = bread.isLocked(); if (locked) { water.unlock(); continue; } // 尝试获得 bread 锁 boolean b = bread.tryLock(10, TimeUnit.SECONDS); // 如果没有获取到 bread 锁,释放 water 锁,再次尝试! if (!b) { water.unlock(); continue; } System.out.println("拿到面包了"); break; } System.out.println("喝水,吃面包!"); } catch (InterruptedException e) { System.out.println("线程中断"); } finally { bread.unlock(); water.unlock(); } }); executorService.shutdown(); } }
程序运行结果:
Pid is:8788
jstack 8788
拿到面包了,等着拿水。。。
拿到水了,等着拿面包。。。
拿到面包了,等着拿水。。。
拿到水了,等着拿面包。。。
拿到面包了,等着拿水。。。
拿到面包了,等着拿水。。。
拿到水了,等着拿面包。。。
已经输出打印了 Pid,尝试自己用 jstack 调试一下吧!可以参考如何排查死锁的章节~
锁让出的时候添加随机睡眠时间
修改上面的程序,参考下面的代码:
executorService.submit( () -> { try { // 尝试获得 bread 锁 while (bread.tryLock(10, TimeUnit.SECONDS)) { System.out.println("拿到面包了,等着拿水。。。"); TimeUnit.SECONDS.sleep(1); // 判断 water 是否被锁住,如果锁住,退出!再次尝试获取 water 锁 boolean locked = water.isLocked(); if (locked) { bread.unlock(); // 避免活锁 TimeUnit.MILLISECONDS.sleep(1000); continue; } // 尝试获得 water 锁 boolean w = water.tryLock(10, TimeUnit.SECONDS); // 如果没有获取到 water 锁,释放 bread 锁,再次尝试! if (!w) { bread.unlock(); continue; } System.out.println("拿到水了"); break; } System.out.println("吃面包,喝水!"); } catch (InterruptedException e) { System.out.println("线程中断"); } finally { water.unlock(); bread.unlock(); } }); executorService.submit( () -> { try { while (water.tryLock(10, TimeUnit.SECONDS)) { System.out.println("拿到水了,等着拿面包。。。"); TimeUnit.SECONDS.sleep(2); // 判断 bread 是否被锁住,如果锁住,退出!再次尝试获取 bread 锁 boolean locked = bread.isLocked(); if (locked) { water.unlock(); // 避免活锁 TimeUnit.MILLISECONDS.sleep(1500); continue; } // 尝试获得 bread 锁 boolean b = bread.tryLock(10, TimeUnit.SECONDS); // 如果没有获取到 bread 锁,释放 water 锁,再次尝试! if (!b) { water.unlock(); continue; } System.out.println("拿到面包了"); break; } System.out.println("喝水,吃面包!"); } catch (InterruptedException e) { System.out.println("线程中断"); } finally { bread.unlock(); water.unlock(); } }); executorService.shutdown();
程序输出结果:
Pid is:18256
jstack 18256
拿到面包了,等着拿水。。。
拿到水了,等着拿面包。。。
拿到面包了
喝水,吃面包!
拿到面包了,等着拿水。。。
拿到水了
吃面包,喝水!Process finished with exit code 0