Java死锁问题
来源:互联网 发布:业务元数据 编辑:程序博客网 时间:2024/06/05 02:02
一般造成死锁必须同时满足如下4个条件:
1,互斥条件:线程使用的资源必须至少有一个是不能共享的;
2,请求与保持条件:至少有一个线程必须持有一个资源并且正在等待获取一个当前被其它线程持有的资源;
3,非剥夺条件:分配资源不能从相应的线程中被强制剥夺;
4,循环等待条件:第一个线程等待其它线程,后者又在等待第一个线程。
1,互斥条件:线程使用的资源必须至少有一个是不能共享的;
2,请求与保持条件:至少有一个线程必须持有一个资源并且正在等待获取一个当前被其它线程持有的资源;
3,非剥夺条件:分配资源不能从相应的线程中被强制剥夺;
4,循环等待条件:第一个线程等待其它线程,后者又在等待第一个线程。
因为要产生死锁,这4个条件必须同时满足,所以要防止死锁的话,只需要破坏其中一个条件即可。
以生产者和消费者为例解决线程死锁:
在jdk中对于Object.wait有这样的一段解释:当前线程必须拥有此对象监视器。该线程放弃对此监视器的所有权并等待,直到其他线程通过调用notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。可见下面一种情况就可能出现:消费者
1
进入等待状态(此时资源锁已被放开),如果此时消费者
2
获取到了资源同步锁(没有人保证消费者
1
进入等待,下一个拿到锁的一定是生产者),消费者
2
判断没有资源也进入等待状态;此时生产者
1
生产了,并notify了消费者
1
,消费者
1
顺利地消费了,并执行notify操作,但此时消费者
2
却也因为资源而处于等待状态,从而唤醒了消费者
2
(消费者
1
本欲唤醒其他生产者),而此时并没有任何资源,导致了整个程序因为消费者
2
陷入无限的等待,形成了死锁。
经过以上分析,究其根本原因是:同时几个消费者或几个生产者处于等待状态,导致消费者可能唤醒的还是消费者,或者生产者唤醒的还是生产者。那么如果我们能够保证同时只有一个消费者处于wait状态(生产者同理),那就就能保证消费者唤醒的一定是生产者,从而能使整个任务顺利进行下去。下面是修改后的代码:
改进后的Consumer.java
package
CreatorAndConsumer;
public
class
Consumer
implements
Runnable {
/**
* 线程资源
*/
private
Plate plate;
/**
* 生产者锁:用于锁定同一时间只能有一个生产者进入生产临界区(如果同时又两个生产者进入临界区,那么很有可能其中一个生产者本想唤醒消费者却唤醒了生产者)
*/
private
static
Object consumerLocker =
new
Object();
public
Consumer(Plate plate) {
this
.plate = plate;
}
@Override
public
void
run() {
// 必须先获得生产者锁才能生产
synchronized
(consumerLocker) {
synchronized
(plate) {
// 如果此时蛋的个数大于0,则等等
while
(plate.getEggNum() <
1
) {
try
{
// 这个细节需要注意,如果线程进入wait,那么其上的锁就会暂时得到释放,
// 不然其他线程也不能进行加锁,然后唤醒本线程
plate.wait();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
// 唤醒后,再次得到资源锁,且条件满足就可以放心地取蛋了
plate.getEgg();
plate.notify();
}
}
}
}
package
CreatorAndConsumer;
/**
* 生产者
*
* @author Martin
*
*/
public
class
Creator
implements
Runnable {
/**
* 线程资源
*/
private
Plate plate;
/**
* 生产者锁:用于锁定同一时间只能有一个生产者进入生产临界区(如果同时又两个生产者进入临界区,那么很有可能其中一个生产者本想唤醒消费者却唤醒了生产者)
*/
private
static
Object creatorLocker =
new
Object();
public
Creator(Plate plate) {
this
.plate = plate;
}
@Override
public
void
run() {
//必须先获得生产者锁才能生产
synchronized
(creatorLocker) {
synchronized
(plate) {
// 如果此时蛋的个数大于0,则等等
while
(plate.getEggNum() >=
5
) {
try
{
// 这个细节需要注意,如果线程进入wait,那么其上的锁就会暂时得到释放,
// 不然其他线程也不能进行加锁,然后唤醒本线程
plate.wait();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
// 唤醒后,再次得到资源锁,且条件满足就可以放心地生蛋啦
Object egg =
new
Object();
plate.addEgg(egg);
plate.notify();
}
}
}
}
测试类:
public
class
Tester {
public
static
void
main(String[] args)
{
//共享资源
Plate plate =
new
Plate();
//添加生产者和消费者
for
(
int
i =
0
; i <
100
; i ++)
{
new Thread(new Creator(plate)).start();
new Thread(new Consumer(plate)).start();
}
}
}
改进说明:改进后的生产者和消费者分别加了生产者锁和消费者锁,分别用于锁定同一时间只能有一个消费者(生产者)进入生产临界区(如果同时又两个生产者进入临界区,那么很有可能其中一个生产者本想唤醒消费者却唤醒了生产者),总的来说就是一共三个锁:消费者锁、生产者锁、生产者和消费者共享的锁。
最终,写多线程的时候需要注意的是一个资源可能唤醒的是所有因该资源而等待的线程,因此消费者线程不一定唤醒的就是生产者线程也可能是消费者线程。
0 0
- java 同步死锁问题
- java 多线程死锁问题
- java多线程死锁问题
- Java死锁问题
- java线程死锁问题
- java线程死锁问题
- Java中的死锁问题
- JAVA中的死锁问题
- Java死锁问题分析
- java死锁问题
- 死锁问题的代码(JAVA)
- java中死锁的问题
- Java语言模拟死锁问题
- Java基础----一个死锁问题
- java 死锁问题 代码实现
- Java同步中的死锁问题
- Java多线程死锁问题测试
- Java 多线程同步、死锁问题
- Android-Text and Input
- UITableviewCell使用以及自定义高度
- UIPickerView常用属性和方法总结
- SpringMVC入门学习(二)应用注解方式+注解优化
- vs2010 配置DirectShow
- Java死锁问题
- 向量范数
- Android 使用NineOldAndroids实现绚丽的ListView左右滑动删除Item效果
- CocoaPods安装使用及配置私有库
- mac下cocoaPods的安装和使用
- 为什么 Java 中要使用 Checked Exceptions
- xcode编译器问题——参数类型识别错误,EXC_BAD_ACCESS
- cer和pfx区别
- Learning Python(7)--Someting Useful