一、死锁的定义:
所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进 。
二、死锁产生的必要条件:
**互斥条件:**线程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某 资源仅为一个线程所占有。此时若有其他线程请求该资源,则请求线程只能等待。
**不剥夺条件:**线程所获得的资源在未使用完毕之前,不能被其他线程强行夺走,即只能由获得该资源的线程自己来释放(只能是主动释放)。
**请求和保持条件:**线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
**循环等待条件:**存在一种线程资源的循环等待链,链中每一个线程已获得的资源同时被链中下一个线程所请求。即存在一个处于等待状态的线程集合{Pl, P2, …, pn},其中Pi等待的资源被P(i+1)占有(i=0, 1, …, n-1),Pn等待的资源被P0占有,如图所示:
产生死锁的一个例子代码:
1.package itheima.com;
2./**
3.* 一个简单的死锁类
4.* 当 DeadLock 类的对象 flag==1 时(td1),先锁定 o1,睡眠 500 毫秒
5.* 而 td1 在睡眠的时候另一个 flag==0 的对象(td2)线程启动,先锁定 o2,睡眠 500 毫秒
6.* td1 睡眠结束后需要锁定 o2 才能继续执行,而此时 o2 已被 td2 锁定;
7.* td2 睡眠结束后需要锁定 o1 才能继续执行,而此时 o1 已被 td1 锁定;
8.* td1、td2 相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。
9.*/
10.public class DeadLock implements Runnable {
11. public int flag = 1;
12. //静态对象是类的所有对象共享的
13. private static Object o1 = new Object(), o2 = new Object();
14. public void run() {
15. System.out.println("flag=" + flag);
16. if (flag == 1) {
17. synchronized (o1) {
18. try {
19. Thread.sleep(500);
20. } catch (Exception e) {
21. e.printStackTrace();
22. }
23. synchronized (o2) {
24. System.out.println("1");
25. }
26. }
27. }
28. if (flag == 0) {
29. synchronized (o2) {
30. try {
31. Thread.sleep(500);
32. } catch (Exception e) {
33. e.printStackTrace();
34. }
35. synchronized (o1) {
36. System.out.println("0");
37. }
38. }
39. }
40. }
41. public static void main(String[] args) {
42. DeadLock td1 = new DeadLock();
43. DeadLock td2 = new DeadLock();
44. td1.flag = 1;
45. td2.flag = 0;
46. //td1,td2 都处于可执行状态,但 JVM 线程调度先执行哪个线程是不确定的。 47. //td2 的 run()可能在 td1 的 run()之前运行
48. new Thread(td1).start();
49. new Thread(td2).start();
50. }
51.}
如何避免死锁 :
在有些情况下死锁是可以避免的。两种用于避免死锁的技术:
1)加锁顺序(线程按照一定的顺序加锁)
1.package itheima.com;
2.public class DeadLock {
3. public int flag = 1;
4. //静态对象是类的所有对象共享的
5. private static Object o1 = new Object(), o2 = new Object();
6. public void money(int flag) {
7. this.flag=flag;
8. if( flag ==1){
9. synchronized (o1) {
10. try {
11. Thread.sleep(500);
12. } catch (Exception e) {
13. e.printStackTrace();
14. }
15. synchronized (o2) {
16. System.out.println("当前的线程是"+
17. Thread.currentThread().getName()+" "+"flag 的值"+"1"); 18. }
19. }
20. }
21. if(flag ==0){
22. synchronized (o2) {
23. try {
24. Thread.sleep(500);
25. } catch (Exception e) {
26. e.printStackTrace();
27. }
28. synchronized (o1) {
29. System.out.println("当前的线程是"+
30. Thread.currentThread().getName()+" "+"flag 的值"+"0"); 31. }
32. }
33. }
34. }
35.
36. public static void main(String[] args) {
37. final DeadLock td1 = new DeadLock();
38. final DeadLock td2 = new DeadLock();
39. td1.flag = 1;
40. td2.flag = 0;
41. //td1,td2 都处于可执行状态,但 JVM 线程调度先执行哪个线程是不确定的。 42. //td2 的 run()可能在 td1 的 run()之前运行
43. final Thread t1=new Thread(new Runnable(){
44. public void run() {
45. td1.flag = 1;
46. td1.money(1);
47. }
48. });
49. t1.start();
50. Thread t2= new Thread(new Runnable(){
51. public void run() {
52. // TODO Auto-generated method stub
53. try {
54. //让 t2 等待 t1 执行完
55. t1.join();//核心代码,让 t1 执行完后 t2 才会执行
56. } catch (InterruptedException e) {
57. // TODO Auto-generated catch block
58. e.printStackTrace();
59. }
60. td2.flag = 0;
61. td1.money(0);
62. }
63. });
64. t2.start();
65. }
66.}
结果:
当前的线程是 Thread-0 flag 的值 1
当前的线程是 Thread-1 flag 的值 0
2)加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
1. package itheima.com;
2.import java.util.concurrent.TimeUnit;
3.import java.util.concurrent.locks.Lock;
4.import java.util.concurrent.locks.ReentrantLock;
5.public class DeadLock {
6. public int flag = 1;
7. //静态对象是类的所有对象共享的
8. private static Object o1 = new Object(), o2 = new Object();
9. public void money(int flag) throws InterruptedException {
10. this.flag=flag;
11. if( flag ==1){
12. synchronized (o1) {
13. Thread.sleep(500);
14. synchronized (o2) {
15. System.out.println("当前的线程是"+
16. Thread.currentThread().getName()+" "+"flag 的值"+"1"); 17. }
18. }
19. }
20. if(flag ==0){
21. synchronized (o2) {
22. Thread.sleep(500);
23. synchronized (o1) {
24. System.out.println("当前的线程是"+
25. Thread.currentThread().getName()+" "+"flag 的值"+"0"); 26. }
27. }
28. }
29. }
30.
31. public static void main(String[] args) {
32. final Lock lock = new ReentrantLock();
33. final DeadLock td1 = new DeadLock();
34. final DeadLock td2 = new DeadLock();
35. td1.flag = 1;
36. td2.flag = 0;
37. //td1,td2 都处于可执行状态,但 JVM 线程调度先执行哪个线程是不确定的。 38. //td2 的 run()可能在 td1 的 run()之前运行
39.
40. final Thread t1=new Thread(new Runnable(){
41. public void run() {
42. // TODO Auto-generated method stub
43. String tName = Thread.currentThread().getName();
44.
45. td1.flag = 1;
46. try {
47. //获取不到锁,就等 5 秒,如果 5 秒后还是获取不到就返回 false 48. if (lock.tryLock(5000, TimeUnit.MILLISECONDS)) {
49. System.out.println(tName + "获取到锁!");
50. } else {
51. System.out.println(tName + "获取不到锁!");
52. return;
53. }
54. } catch (Exception e) {
55. e.printStackTrace();
56. }
57.
58. try {
59. td1.money(1);
60. } catch (Exception e) {
61. System.out.println(tName + "出错了!!!");
62. } finally {
63. System.out.println("当前的线程是"+Thread.currentThread().getName()+"释放锁!! ");
64. lock.unlock();
65. }
66. }
67. });
68. t1.start();
69. Thread t2= new Thread(new Runnable(){
70. public void run() {
71. String tName = Thread.currentThread().getName();
72. // TODO Auto-generated method stub
73. td1.flag = 1;
74. try {
75. //获取不到锁,就等 5 秒,如果 5 秒后还是获取不到就返回 false 76. if (lock.tryLock(5000, TimeUnit.MILLISECONDS)) {
77. System.out.println(tName + "获取到锁!");
78. } else {
79. System.out.println(tName + "获取不到锁!");
80. return;
81. }
82. } catch (Exception e) {
83. e.printStackTrace();
84. }
85. try {
86. td2.money(0);
87. } catch (Exception e) {
88. System.out.println(tName + "出错了!!!");
89. } finally {
90. System.out.println("当前的线程是"+Thread.currentThread().getName()+"释放锁!!");
91. lock.unlock();
92. }
93. }
94. });
95. t2.start();
96. }
97.}
打印结果:
Thread-0 获取到锁:
当前的线程是 Thread-0 flag 的值 1
当前的线程是 Thread-0 释放锁::
Thread-1 获取到锁:
当前的线程是 Thread-1 flag 的值 0
当前的线程是 Thread-1 释放锁::