关于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并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够.于是再次翻看了一下书里的内容,突然有点打开脑门的感觉.看来确实是要学习的最好方式 ...
随机推荐
- Unity容器中AOP应用示例程序
转发请注明出处:https://www.cnblogs.com/zhiyong-ITNote/p/9127001.html 实在没有找到Unity容器的AOP应用程序示例的说明,在微软官网找到了教程( ...
- elasticsearch数据备份还原
elasticsearch数据备份还原 1.在浏览器中运行http://XXX.XXX.XXX.XXX:9200/_flush,确保索引数据能保存到硬盘中. 2.原数据的备份.主要是elasticse ...
- 输出日文CSV乱码问题
直接写用Excel打开时会乱码,需要加上下面代码中注释的三行 fos = new FileOutputStream(file, false); //fos.write( 0xef ); //fos.w ...
- AE插入音乐
将音乐文件(如MP3文件)直接拖拽到工程里就可以. 然后添加到合成里. 点击内容预览 ,就自动播放了. 在合成窗口里面,我们可以对于音频文件进行拖动,以及裁剪等操作,但是需要注意的是AE里面不能预览声 ...
- Codeforces.468C.Hack it!(构造)
题目链接 \(dls\)出的比赛诶...这么妙. \(Description\) 令\(f(x)\)表示整数\(x\)在十进制下各个数位的数字之和.给定\(a\),求两个整数\(l,r\),使得\(\ ...
- Android 如何判断指定服务是否在运行中 “Service”
如何判断一个服务是否正在运行中: /** * 判断某个服务是否正在运行的方法 * * @param mContext * @param serviceName 是包名+服务的类名 * @return ...
- 【DWM1000】 code 解密8一 TAG接收blink response 信号
在分析这个部分前,目前我看到DWM1000 的资料,data可以分为blink和一般无线数据,后面有内容我们再扩充, 上面我们已经看到接收到blink触发的事件为 case SIG_RX_BLINK ...
- 小型资源管理器之动态添加TreeView节点
FrmMain主界面 using System; using System.Collections.Generic; using System.ComponentModel; using System ...
- 编程菜鸟的日记-初学尝试编程-C++ Primer Plus 第4章编程练习3
#include <iostream>#include <cstring>using namespace std;int main(){ char fname[20]; cha ...
- yii创建控制台命令
创建控制台命令程序1.控制台命令继承自 yii\console\Controller控制器类2.在控制器类中,定义一个或多个动作,动作与控制台子命令相对应3.在动作方法中实现业务需求的代码 运行控制台 ...