线程死锁

作者: zhaochenxi 分类: 并发 发布时间: 2017-05-05 11:31

多线程编程的过程中线程在竞争资源的时候很容易造成死锁,那么什么是线程的死锁,应该怎么避免线程的死锁呢?

死锁

死锁是多个线程之间因为竞争资源而导致的陷入循环等待对方释放掉占有资源的锁的一种状态,如图所示:

有两个线程,线程A已经占有了资源A,但是现在想要资源B,但是现在线程B已经占有了资源B,也还想要资源A,于是A,B都在等待对方释放自己想要的资源,并且都不愿意释放自已占有的资源,所以一直等待,这样就导致了死锁。

在这种情况下如果没有外力作用,那么死锁会一直持续下去。

死锁的必要条件

产生死锁需要满足四个必要条件,只要其中一个不满足死锁就不会发生。

1.互斥条件:即某个资源在一段时间内只能由一个线程占有,不能同时被两个或两个以上的线程占有。

2.不可抢占条件:线程占有的资源在未使用完毕之前,资源申请者不能强行地从资源占有者手中夺取资源,而只能由该资源的占有者自行释放

3.占有且申请条件:线程至少已经占有一个资源,但又申请新的资源;由于该资源已被其他线程占有,此时该线程阻塞;但是,它在等待新资源之时,仍继续占用已占有的资源。

4.循环等待条件:存在一个线程等待序列{P1,P2,…,Pn},其中P1等待P2所占有的某一资源,P2等待P3所占有的某一源,……,而Pn等待P1所占有的的某一资源,形成一个线程循环等待环。

下面我们来看一个死锁代码实例:

package com.naruto.thread.deadlock;

public class DeadLockThread implements Runnable {

	private String objA;
	private String objB;

	public DeadLockThread(String objA, String objB) {
		this.objA = objA;
		this.objB = objB;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		String name = Thread.currentThread().getName();
		synchronized (objA) {
			System.out.println(name + " 获得 " + objA);
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(name + " 申请 " + objB);
			synchronized (objB) {
				System.out.println(name + " 获得 " + objB);
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(name + " 释放 " + objA);
			}
			System.out.println(name + " 释放 " + objA);
		}

	}

}

执行死锁线程代码:

package com.naruto.thread.deadlock;

public class Test {

	public static void main(String[] args) throws InterruptedException {
		String A = "A";
		String B = "B";
		Thread threadA = new Thread(new DeadLockThread(A, B),"ThreadA");
		Thread threadB = new Thread(new DeadLockThread(B, A),"ThreadB");
		threadA.start();
		threadB.start();
	}

}

执行输出:

ThreadB 获得 B

ThreadA 获得 A

ThreadA 申请 B

ThreadB 申请 A

程序一直处于死锁状态,线程A在等待线程B释放B,线程B在等待线程A释放A,所以发生了死锁。

以上程序刚好满足死锁的是个必要条件。在死锁线程中,我使用了synchronized的嵌套来模拟死锁的“占有并申请”条件,在实际的编程中很少出现这种情况,如果使用synchronized的时候使用了这种嵌套一定要注意,基本上是会发生死锁的。这种不恰当用法很容易发现,但是要注意使用Lock对象的时候一定要记得,在调用了lock.lock()加锁之后,执行完任务之后一定记得及时调用lock.unlock()释放锁之后才去申请其他资源的锁,否则就和示例程序一样会发生死锁了。

避免死锁

发生死锁需要满足四个必要条件,所以如果我们要避免死锁的发生,那么只要让我们的程序不要满足死锁发生的必要条件即可。

1.破坏互斥条件:使用共享资源,不加锁,这种方法在实际中并没有什么用,因为有的资源就是一种竞争性资源。

2.允许资源抢占。具体的做法是如果一个线程需要获取的资源被其他线程占有了,需要等待释放,那么该线程直接释放掉了自身所占有的全部资源,让这些资源可以被其他线程占有,等到需要的资源能够获取到的时候在来获取资源执行任务。

3.破坏占有且申请条件。

(1)在申请获得其他资源的锁的时候先放掉已经占有的资源的锁

(2)一次性申请自己需要的所有资源,如果不能满足,那么就等待直到满足了才执行任务。这种方案的资源利用率很低,一个线程在运行期间占用了一整块资源,其中的某些资源利用完了之后没有及时释放,其他的线程不能获得这个资源。

4.破坏循环等待条件。让资源有序的分配,避免形成资源分配的闭环。


    

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注