java线程研究---(10)Thread同步:死锁

来源:互联网 发布:知金教育的含金量如何 编辑:程序博客网 时间:2024/05/21 17:00

死锁


synchronized块的乱用,会造成死锁。

下面来看看死锁形成的大致情况:

  • 线程T1,执行线程对象A
  • 线程T2,执行线程对象B
  • 线程对象A,里面有加锁的代码需要获得自己的锁,并且同时有另外一个加锁的代码需要获得线程对象B的锁。
  • 线程对象B,里面有加锁的代码需要获得自己的锁,并且同时有另外一个加锁的代码需要获得线程对象A的锁。
  • 简言之:
    • 线程对象A在获得自己的锁的时候,需要获得线程对象B的锁
    • 线程对象B在获得自己的锁的时候,需要获得线程对象A的锁
  • 那么这两个对象都在占用自己的锁的时候,想要获得对方对象的锁。
  • 因此,这两个对象都在等锁的池(lock pool)中,都处于等待拿锁的状态。
  • 所以死锁就形成了——两个对象会无限的等待下去。。。

来看看死锁示例代码:

DeadThreadA.java

package thread;public class DeadThreadA implements Runnable {public DeadThreadBdeadThreadB;// 想拿到对方线程对象的锁,就得先获得引用private inti= 0;private intj= 0;@Overridepublic void run() {while (true) {synchronized (this) { // 首先占用自己对象的锁if (i >= 10)break;i++;for (int j = 0; j < 10000000l; j++);System.out.println(Thread.currentThread().getName() + ": i=" + i);while (true) {synchronized (deadThreadB) {  // 在占用自己对象的锁的同时,需要获得deadThreadB的锁if (j >= 10) {break;}j++;for (int j = 0; j < 10000000l; j++);System.out.println(Thread.currentThread().getName() + ": j=" + j);}}}}}}

DeadThreadB.java

package thread;public class DeadThreadB implements Runnable {public DeadThreadAdeadThreadA;// 想拿到对方线程对象的锁,就得先获得引用private inti= 0;private intj= 0;@Overridepublic void run() {while (true) {synchronized (this) { // 首先占用自己对象的锁if (i >= 10)break;i++;for (int j = 0; j < 10000000l; j++);System.out.println(Thread.currentThread().getName() + ": i= " + i);while (true) {synchronized (deadThreadA) { // 在占用自己对象的锁的同时,需要获得deadThreadA的锁if (j >= 10) {break;}j++;for (int j = 0; j < 10000000l; j++);System.out.println(Thread.currentThread().getName() + ": j= " + j);}}}}}}

代码的几点说明:

  • synchronized()块,只支持传入对象,如果想拿对方线程对象的锁,那么首先得获得对方线程对象的引用。
  • 当前的两个 线程对象,首先是各自已经占用了自己的锁,同时再,都尝试去拿对方的锁。
  • 所以,先synchronized (this),在占用自己的锁的同时,(没有释放自己的锁)再去拿对方的锁:synchronized (deadThreadB), synchronized (deadThreadA)
  • 也就是说,当前线程,要同时拿两个锁:首先是拿到自己锁,然后再去尝试拿对方的锁。
执行类,如下

DeadThreadMain.java

package thread;public class DeadThreadMain {public static void main(String ab[]) {DeadThreadA a = new DeadThreadA();DeadThreadB b = new DeadThreadB();b.deadThreadA = a; // 获得对方线程对象的引用a.deadThreadB = b; // 获得对方线程对象的引用Thread t1 = new Thread(a); Thread t2 = new Thread(b);t1.start();t2.start();}}


控制台就打印这么多,之后程序是一直运行的,但是就像死循环一样,永不停止了:
Thread-1: i= 1
Thread-0: i=1



根据示例代码,来具体讲一下死锁过程:
  • t1线程启动的时候,首先占用了a对象的锁
  • t2线程启动的时候,首先占用了b对象的锁
  • t1线程占用了a对象的锁的同时,尝试拿b对象的锁:b对象的锁,一直在t2线程手里
  • t2线程占用了b对象的锁的同时,尝试拿a对象的锁:a对象的锁,一直在t1线程手里
  • t1和t2线程都处于等待拿对方的锁的状态,
  • 死锁产生








0 0
原创粉丝点击