什么情况会发生死锁?过多的同步方法会造成死锁
一旦有多个进程,且它们都要争用对多个锁的独占访问,那么就有可能发生死锁。如果有一组进程或线程,其中每个都在等待一个只有其它进程或线程才可以执行的操作,那么就称它们被死锁了。
最常见的死锁形式是当线程 1 持有对象 A 上的锁,而且正在等待对象 B 上的锁;而线程 2 持有对象 B 上的锁,却正在等待对象 A 上的锁。这两个线程永远都不会获得第二个锁,或是释放第一个锁,所以它们只会永远等待下去。
例如:黑帮交货
A:你先给钱,我才给货
B:你先给货,我再给钱
.....死锁了....
实例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
public
class
LockDemo1 {
public
static
void
main(String[] args) {
Object g =
new
Object();
Object m =
new
Object();
Test1 t1 =
new
Test1(g,m);
Test1 t2 =
new
Test1(g,m);
Thread thread1 =
new
Thread(t1);
Thread thread2 =
new
Thread(t2);
thread1.start();
thread2.start();
}
}
//线程1:先给钱
class
Test1
implements
Runnable{
Object goods;
Object money;
public
Test1(Object goods, Object money) {
this
.goods = goods;
this
.money = money;
}
@Override
public
void
run() {
while
(
true
) {
test();
}
}
public
void
test(){
synchronized
(goods) {
try
{
Thread.sleep(
100
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
synchronized
(money) {
}
}
System.out.println(
"先给钱、、"
);
}
}
//线程2:先给货
class
Test2
implements
Runnable{
Object goods;
Object money;
public
Test2(Object goods, Object money) {
this
.goods = goods;
this
.money = money;
}
@Override
public
void
run() {
while
(
true
) {
test();
}
}
public
void
test(){
synchronized
(money) {
try
{
Thread.sleep(
500
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
synchronized
(goods) {
}
}
System.out.println(
"先给货、、"
);
}
}
|
这样运行的话有可能被锁定,记住:并不是每次都是死锁的
生产者消费者模式(Producer-consumer problem)并不是设计模式:也称有限缓冲问题。
既然是生产者消费者模式,那必然有生产者类和消费者类,然后还有一个共享的资源(因为多个线程对同一份资源都需要锁释放所以才造成死锁)
所以,至少需要4个类:这里以在电影院看电影为例子说明生产者和消费者模式
共享资源(电影):Movie
生产者(电影院):Player
消费者(看电影的人):Watcher
测试类:App
1、未使用生产者消费者模式情况
共享资源类:Movie
1
2
3
4
5
6
7
8
9
10
11
12
|
//共享资源
public
class
Movie {
private
String pic ;
//播放
public
void
play(String pic){
this
.pic = pic;
}
//观看
public
void
watch(){
System.out.println(pic);
}
}
|
生产者类:Player
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//生产者:生产电影
public
class
Player
implements
Runnable {
private
Movie m;
//共同使用的资源
public
Player(Movie m) {
super
();
this
.m = m;
}
@Override
public
void
run() {
for
(
int
i =
0
; i <
20
; i++) {
if
(
0
==i%
2
) {
m.play(
"左青龙"
);
}
else
{
m.play(
"右白虎"
);
}
}
}
}
|
消费者类:Watcher
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//消费者:观看电影
public
class
Watcher
implements
Runnable{
private
Movie m;
//共同使用的资源
public
Watcher(Movie m) {
super
();
this
.m = m;
}
@Override
public
void
run() {
for
(
int
i =
0
; i <
20
; i++) {
m.watch();
}
}
}
|
测试代码:App
1
2
3
4
5
6
7
8
9
|
public
class
App {
public
static
void
main(String[] args) {
Movie m=
new
Movie();
Player p =
new
Player(m);
Watcher w =
new
Watcher(m);
new
Thread(p).start();
new
Thread(w).start();
}
}
|
目前还没有加入生产者消费者模式,测试结果:打印的全是右白虎
右白虎
右白虎
.............
加入现在加上锁,在共享资源上都加上锁再试试
1
2
3
4
5
6
7
8
9
10
11
|
public
class
Movie {
private
String pic ;
//播放
public
synchronized
void
play(String pic){
//加锁
this
.pic = pic;
}
//观看
public
synchronized
void
watch(){
//加锁
System.out.println(pic);
}
}
|
再次测试,结果还是只有右白虎。
2、加入生产者消费者模式:使用信号灯法
对共享资源上加入flag标志来控制共享的资源
flag=true:生产者生产,消费者等待【Object对象的wait()】。生产完成后,通知消费者消费【Object对象的notify()】
flag=false:消费者消费,生产者等待【Object对象的wait()】。消费完成后,通知生产者生产【Object对象的notify()】
这里用到了等待wait()和通知notify(),这俩都是Object对象的方法 。wait:会释放锁 Thread.sleep():不会释放锁【抱着锁睡觉】
接下来修改共享资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
public
class
Movie {
private
String pic ;
/**
* flag=true:生产者生产,消费者等待【Object对象的wait()】。生产完成后,通知消费者消费【Object对象的notify()】
* flag=false:消费者消费,生产者等待【Object对象的wait()】。消费完成后,通知生产者生产【Object对象的notify()】
*/
private
boolean
flag=
true
;
//信号灯
//播放:生产者
public
synchronized
void
play(String pic){
if
(!flag) {
//生产者等待
try
{
this
.wait();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
//开始生产
try
{
Thread.sleep(
500
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
//生产完毕
this
.pic = pic;
System.out.println(
"生产了:"
+pic);
//通知消费者消费
this
.notify();
//生产者停止生产(让其等待):因为刚生产完了就不必生产了
this
.flag =
false
;
}
//观看:消费者
public
synchronized
void
watch(){
if
(flag) {
//消费者等待
try
{
this
.wait();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
//开始消费
try
{
Thread.sleep(
200
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"消费了:"
+pic);
//消费完毕
//通知生产者可以生产了
this
.notifyAll();
//消费停止(让其等待):因为已经消费完毕了
this
.flag =
true
;
}
}
|
再次测试:
生产了:左青龙
消费了:左青龙
生产了:右白虎
消费了:右白虎
测试结果很规律:生产了什么就消费什么,等消费完成了就继续再生产...............没有出现死锁问题
1、什么情况下会出现死锁?
当线程 1 持有对象 A 上的锁,而且正在等待对象 B 上的锁;而线程 2 持有对象 B 上的锁,却正在等待对象 A 上的锁。这两个线程永远都不会获得第二个锁,或是释放第一个锁,所以它们只会永远等待下去。
过多的使用同步会出现死锁
2、死锁的解决办法?
使用生产者消费者模式【该模式不是一个设计模式,而是为了解决死锁而出现的一种策略】
生产者消费者模式使用之一:信号灯法
在共享资源上加入flag标志来控制当生产者生产的时候,消费者等待。
当消费者消费的时候,生产者等待。
3、wait()、notify()、notifyAll()都是Object对象的方法,所以可以直接使用