java 活锁,死锁的简单例子Java

  java 活锁,死锁的简单例子Java

  00-1010活锁和死锁活锁死锁的四个必要条件,互斥条件,请求保持条件,非剥夺条件,循环等待条件,死锁实例,死锁故障排除,总结如何避免死锁,防止死锁,设置锁定顺序,活锁实例,解决活锁

  

目录

 

  00-1010活锁也会发生在多个协作线程之间。当它们为了回应对方而互相礼让,以至于没有一个线程能继续前进的时候,活锁就会发生。像死锁一样,发出生命锁的线程不能继续执行。

  就相当于两个半路相遇的人:互相礼让,避开对方的路,却又在另一条路上相遇。就这样,一直回避。

  00-1010两个或多个线程阻塞了死锁中其他线程持有的锁。当多个线程同时请求同一组锁,但顺序不同时,通常会发生死锁。死锁会使你的程序挂起,无法完成任务。

  

活锁与死锁

在Java中使用多线程会导致有可能导致死锁问题。死锁会使程序一直停留在,程序不会进一步执行。我们只能用中止并重启s的方式重新执行程序。这是我们不愿意看到的现象。我们希望尽可能避免僵局!

 

  00-1010是指进程独占使用分配的资源,即一个资源在一定时间内只被一个进程占用。如果此时有其他进程请求资源,那么请求者只能等待,直到占用资源的进程用完并被释放。

  00-1010表示一个进程已经保持了至少一个资源,但是提出了新的资源请求,并且这个资源已经被其他进程占用。此时,发出请求的进程被阻塞,但是它继续持有它已经获得的其他资源。

  00-1010是指进程获得的资源,在用完之前不能被剥夺,用完了才能自己释放。

  00-1010意味着当死锁发生时,必须存在一个循环链的进程——的资源,即进程集合{A,B,C,Z}在等待一个被B占用的资源;b在等待C占用的资源,……,Z在等待a已经占用的资源。

  

活锁

/* * @ author stival */@ suppress warnings( all )公共类DeadLock实现Runnable { public int flag=1;/* * bread */private静态最终对象bread=new object();/* * water */私有静态最终对象water=new object();@ override public void run(){ if(flag==1){//先吃面包再喝水。循环等待条件同步(面包){ 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 lock bread=new DeadLock();DeadLock lock water=new DeadLock();洛克面包

 

  ag = 1; lockWater.flag = 0; // lockBread,lockWater 都处于可执行状态,但 JVM 线程调度先执行哪个线程是不确定的。 // lockWater 的 run() 可能在 lockBread 的 run() 之前运行 new Thread(lockBread).start(); new Thread(lockWater).start(); }}程序运行结果:

  

喝完水,等吃面包了。。。面包吃了,等喝水。。。

 

  

 

  

死锁排查

先执行上面的代码,再进行排查!

 

  

1、使用 jps -l

 

  

D:workspace-codegiteedolphin>jps -l7072 org.jetbrains.jps.cmdline.Launcher8824 sun.tools.jps.Jps172124012 com.dolphin.thread.locks.DeadLock6684 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.

从打印信息我们可以看出:

 

  Found 1 deadlock: 发现一个死锁;Thread-1 locked <0x000000076bf9e3e8> waiting to lock <0x000000076bf9e3d8>:线程1,锁住了 0x000000076bf9e3e8 等待 0x000000076bf9e3d8 锁;Thread-0 locked <0x000000076bf9e3d8> waiting to lock <0x000000076bf9e3e8>:线程0,锁住了 0x000000076bf9e3d8 等待 0x000000076bf9e3e8 锁;

 

  

 

  

总结一下

当 DeadLock 类的对象 flag=1 时(lockBread),先锁定 bread,睡眠500毫秒;而 lockBread 在睡眠的时候另一个 flag==0 的对象(lockWater)线程启动,先锁定 water,睡眠500毫秒;lockBread 睡眠结束后需要锁定 water 才能继续执行,而此时 water 已被 lockWater 锁定;lockWater 睡眠结束后需要锁定 bread 才能继续执行,而此时 bread 已被 lockBread 锁定;lockBread、lockWater 相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁;

 

  

如何避免死锁

 

  

预防死锁

其实就是破坏死锁的四个必要条件!!!

 

  破坏互斥条件:使资源同时访问而非互斥使用,就没有进程会阻塞在资源上,从而不发生死锁。破坏请求和保持条件:采用静态分配的方式,静态分配的方式是指进程必须在执行之前就申请需要的全部资源,且直至所要的资源全部得到满足后才开始执行,只要有一个资源得不到分配,也不给这个进程分配其他的资源。破坏不剥夺条件:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源,但是只适用于内存和处理器资源。破坏循环等待条件:给系统的所有资源编号,规定进程请求所需资源的顺序必须按照资源的编号依次进行。

 

  

设置加锁顺序

两个线程试图以不同的顺序来获得相同的锁,如果按照相同的顺序来请求锁,那么就不会出现循环的加锁依赖性,因此也就不会产生死锁,每个需要锁面包和锁水的线程都以相同的顺序来获取面包和水,那么就不会发生死锁了,如下图所示:

 

  

 

  根据上图我们修改一下之前写的死锁代码,消除死锁!

  

@Overridepublic 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:8788jstack 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:18256jstack 18256拿到面包了,等着拿水。。。拿到水了,等着拿面包。。。拿到面包了喝水,吃面包!拿到面包了,等着拿水。。。拿到水了吃面包,喝水!

 

  Process finished with exit code 0

  

到此这篇关于Java中死锁与活锁的具体实现的文章就介绍到这了,更多相关Java 死锁与活锁内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!

 

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: