关于java中死锁的总结
关于死锁,估计很多程序员都碰到过,并且有时候这种情况出现之后的问题也不是非常好排查,下面整理的就是自己对死锁的认识,以及通过一个简单的例子来来接死锁的发生,自己是做python开发的,但是对于死锁的理解一直是一种模糊的概念,也是想过这次的整理更加清晰的认识这个概念。
用来理解的例子是一个简单的生产者和消费者模型,这里是有一个生产者,有两个消费者,并且注意代码中使用notify方法的代码行
package study_java.ex11; import java.util.LinkedList;
import java.util.List; public class PCDemo1 {
public static void main(String[] args){
Pool pool = new Pool();
Producter p1 = new Producter(pool);
p1.setName("p1");
Consumer c1 = new Consumer(pool);
Consumer c2 = new Consumer(pool);
c1.setName("c1");
c2.setName("c2");
p1.start();
c1.start();
c2.start();
}
} class Pool{
private List<Integer> list = new LinkedList<Integer>();
private int Max = 1;
public void addLast(int n){
String name = Thread.currentThread().getName();
synchronized (this){
while (list.size() >= Max){
try{
System.out.println(name+".wait()");
this.wait();
}
catch (Exception e){
e.printStackTrace();
}
}
System.out.println(name + "+" + n);
list.add(new Integer(n));
System.out.println(name + ".notify()");
this.notify(); // 注意这里是调用的是notify方法
}
}
public int remove(){
String name = Thread.currentThread().getName();
synchronized (this){
while (list.size() == 0){
try{
System.out.println(name + ".wait()");
this.wait();
}
catch (Exception e){
e.printStackTrace();
} }
System.out.println(name + "-" + 0);
int no = list.remove(0);
System.out.println(name + ".notify()");
this.notify(); // 注意这里是调用的是notify方法
return no;
}
} } // 生产者
class Producter extends Thread{
private Pool pool;
static int i = 1;
public Producter(Pool pool){
this.pool = pool;
}
public void run(){
while (true){
pool.addLast(i++);
System.out.println("生产者生产了"+i+"号");
}
} } // 消费者
class Consumer extends Thread{
private Pool pool;
public Consumer(Pool pool){
this.pool = pool;
}
public void run(){
while (true){
int no = pool.remove();
System.out.println("消费者消费了"+no+"号");
}
} }
这段代码的运行效果是日志,在最后程序卡主不动了:
c1.wait()
p1+1
p1.notify()
c1-0
c1.notify()
消费者消费了1号
c1.wait()
生产者生产了2号
p1+2
p1.notify()
c1-0
c1.notify()
消费者消费了2号
c1.wait()
生产者生产了3号
p1+3
p1.notify()
c1-0
c1.notify()
消费者消费了3号
c1.wait()
生产者生产了4号
p1+4
p1.notify()
c1-0
c1.notify()
消费者消费了4号
c1.wait()
生产者生产了5号
p1+5
p1.notify()
c1-0
c1.notify()
消费者消费了5号
c1.wait()
生产者生产了6号
p1+6
p1.notify()
生产者生产了7号
c1-0
c1.notify()
消费者消费了6号
c1.wait()
p1+7
p1.notify()
生产者生产了8号
p1.wait()
c2-0
c2.notify()
消费者消费了7号
c2.wait()
c1.wait()
p1+8
p1.notify()
生产者生产了9号
p1.wait()
c2-0
c2.notify()
消费者消费了8号
c2.wait()
c1.wait()
对上面的出现卡主的情况进行分析,理解为啥会卡主:
从这次的执行效果可以看出第一次是c1抢到了执行权,但是这个时候pool是空
所以c1没有可以消费的对象,被放入到了等待队列
接着p1抢到了执行权,生产了1个,然后p1.notify(),这个时候等待队列里只有c1,所以c1被唤醒,c1消费了1个,然后c1.notify(), 这个时候等待队列也没有等待的,这个时候有被c1抢到了执行权,但是pool里没有可以消费的内容,所以c1.wait() 进入到等待队列
这个时候p1抢到执行权,生产了1个,然后p1.notify(),这个时候等待队列里只有c1,所以c1被唤醒,c1也抢到了执行权,消费了1个,然后c1.notify()
同样这个时候等待队列里没有等待的,c1这次又抢到了执行权,但pool里没有可以消费的内容,所以c1.wait(),进入到等待队列
p1 又抢到了执行权,生产1个,然后p1.notify(),这个时候等待队列里只有c1,所以c1被唤醒,c1也抢到了执行权,消费了1个,然后c1.notify()
同样这个时候等待队列里没有等待的,c1这次又抢到了执行权,但pool里没有可以消费的内容,所以c1.wait(),进入到等待队列
.......这种情况重复了几次
但是运行到下面这段的时候问题出现了:
p1+7
p1.notify()
生产者生产了8号
p1.wait()
c2-0
c2.notify()
消费者消费了7号
c2.wait()
c1.wait()
p1+8
p1.notify()
生产者生产了9号
p1.wait()
c2-0
c2.notify()
消费者消费了8号
c2.wait()
c1.wait()
继续进行分析,中间重复的部分不做分析了,和前面的过程是一样的
这个时候等待队里里依然是c1 这个时候p1抢到了执行权,生产了1个,p1.notify() 这个时候等待队列里只有c1,所以c1被唤醒,但是c1没有抢过p1,p1自己又抢到了执行权,但是这个时候pool里面已经有内容,所以p1没有生产,p1.wait(),p1进入等待队列
这个时候c2抢到了执行权,c2消费1个,c2.notify() 这个时候等待队里是p1,p1被唤醒,但是这个时候c2抢到了执行权,但是pool没有内容可以消费所以c2.wait() 进入等待队列
接着c1抢到了执行权,同样pool没有可以消费的内容,c1.wait() 进入到等待队列
p1这个时候抢到了执行权,p1生产了1个,接着p1.notify() 这个时候等待队列里有c1和c2,但是只有一个会被唤醒,不管是哪个,结果没抢过p1,p1再次拿到执行权,但是这个时候pool已经有内容,所以p1.wait() p1进入等待队列
从下面是c2执行,可以看出刚才是c2被唤醒了,这个时候c2也拿到了执行权消费了1个。c2.notify() 等待队列里这个时候有c1 和p1 但是这个时候c2 自己抢到了执行权,但是没有可以消费的,c2.wait() c2 进入等待队列
不巧的是刚才抢到执行权的正好是c1,所以c1继续wait,再次进入等待队列
到这个时候p1 c1 c2 都进入等待队列里,都在等待唤醒,也就出现了程勋最后卡住不动的情况
解决的方法有两种:
第一种:
其实解决上面的方法也比较简单,就是把调用notify的地方全部换成notifyAll方法
notify和notifyAll的区别是,当执行notifyAll的时候会唤醒所有等待的线程,从而避免之前的都在等待队列等待的问题
第二种:
就是wait()的时候加上超时参数,不是像之前一直傻等,而是在超过既定的时间之后自己唤醒
关于java中死锁的总结的更多相关文章
- Java中死锁的简单例子及其避免
死锁:当一个线程永远地持有一个锁,并且其他线程都尝试获得这个锁时,那么它们将永远被阻塞.比如,线程1已经持有了A锁并想要获得B锁的同时,线程2持有B锁并尝试获取A锁,那么这两个线程将永远地等待下去. ...
- Java中死锁的定位与修复
死锁应该可以说是并发编程中比较常见的一种情况,可以说如果程序产生了死锁那将会对程序带来致命的影响:所以排查定位.修复死锁至关重要: 我们都知道死锁是由于多个对象或多个线程之间相互需要对方锁持有的锁而又 ...
- 浅谈java中死锁问题
知识点:死锁的产生.死锁的实例 一:死锁的产生 我们在解决多线程共享资源的线程同步问题时,会使用synchronized关键字修饰方法或者通过Lock加锁方式修饰方法.代码块,防止多个线程访问统一资源 ...
- JAVA中关于同步与死锁的问题
java中当多个现成同时操纵同一资源的时候需要考虑同步的问题.如车站售票,不同售票点卖同一班次车票的时候就要同步,否则卖票会有问题.下面代码模拟车站卖票: class TicketSeller imp ...
- java多线程--死锁
1. Java中导致死锁的原因 Java中死锁最简单的情况是,一个线程T1持有锁L1并且申请获得锁L2,而另一个线程T2持有锁L2并且申请获得锁L1,因为默认的锁申请操作都是阻塞的,所以线程T1和T2 ...
- java之死锁
转载自 https://www.cnblogs.com/xiaoxi/p/8311034.html 一.死锁的定义 多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力.然而,并发执行也带来 ...
- 死锁线程探讨Java中的死锁现象
题记:写这篇博客要主是加深自己对死锁线程的认识和总结实现算法时的一些验经和训教,如果有错误请指出,万分感谢. 今天搞了一下Java的死锁机制,感到自己还是不怎么懂,所以就从一些简略的源代码中琢磨:我先 ...
- java中多线程产生死锁的原因以及解决意见
1. java中导致死锁的原因 多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结 ...
- java中的锁
java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够.于是再次翻看了一下书里的内容,突然有点打开脑门的感觉.看来确实是要学习的最好方式 ...
随机推荐
- LoRaWAN 1.1 网络协议规范 - 5 MAC指令
LoRaWAN 1.1 网络协议规范 LoRaWAN 1.1 版本封稿很久了也没有完整啃过一遍,最近边啃边翻译,趁着这个机会把它码下来. 如果觉得哪里有问题,欢迎留言斧正. 翻译不易,转载请申明出处和 ...
- JAVAScript:前端模块化开发
目录 一:前端模块化概要 1.1.模块化概要 1.2.函数封装 1.3.对象封装 1.4.立即执行函数表达式(IIFE) 1.5.模块化规范 1.5.1.CommonJS 1.5.2.AMD((Asy ...
- AspectJ 切面注解中五种通知注解:@Before、@After、@AfterRunning、@AfterThrowing、@Around
https://blog.csdn.net/u010502101/article/details/78823056
- C#相关FTP操作
C# 相关 FTP(File Transfer Protocol) 操作 1.获取文件目录 UsePassive:需要设置为false. 使用被动模式,因为客户端防火墙的存在,所以为了确保数据可以正常 ...
- 搭建vue脚手架---vue-cli
vue-cli作为一款mvvm框架语言(vue)的脚手架,集成了webpack环境及主要依赖,对于项目的搭建.打包.维护管理等都非常方便快捷.我们在开发项目时尤其需要这样一个快速构建项目的工具. 以下 ...
- GBT 31000-2015 社会治安综合治理基础数据规范 数据项 编码
动态掌握 各级 综治组织 和 队伍建设 情况. 对 综治组织建设和其他业务 进行 统计. -- 数据描述 包括 序号 数据项名称 数据项类型 数据项长度 是否必填 相关条文和标准 -- 综治机构 包 ...
- C#winform自定义控件大全
对C# WinForm开发系列收集的控件使用方面进行整理, 加入了一些文章, 不断补充充实, 完善这方面. 基础 - 常用控件 C# WinForm开发系列 - CheckBox/Button/Lab ...
- BZOJ.1566.[NOI2009]管道取珠(DP 思路)
BZOJ 洛谷 考虑\(a_i^2\)有什么意义:两个人分别操作原序列,使得得到的输出序列都为\(i\)的方案数.\(\sum a_i^2\)就是两人得到的输出序列相同的方案数. \(f[i][j][ ...
- BZOJ.4650.[NOI2016]优秀的拆分(后缀数组 思路)
BZOJ 洛谷 令\(st[i]\)表示以\(i\)为开头有多少个\(AA\)这样的子串,\(ed[i]\)表示以\(i\)结尾有多少个\(AA\)这样的子串.那么\(Ans=\sum_{i=1}^{ ...
- 为NEO-GUI 添加插件系统
作为一个NEO区块链技术爱好者,经常要摆弄NEOGUI,而NEOGUI在众多开发者手中有了众多的分支实现,我也有自己的分支改版.这是一件很麻烦的事情. 虽然NEO-GUI定位为一个演示客户端与开发工具 ...