线程死锁

线程死锁的现象

两个或两个以上的线程在执行过程中,因争夺资源而造成的互相等待的现象,在无外力作用的情况下,这些线程会一直互相等待而无法继续运行下去.

线程死锁的四个条件

  • 互斥条件
    资源只能被一个线程占用,如果其它线程请求获取该资源,则请求者只能等待,直到占用资源的线程释放该资源.
  • 请求并持有条件
    指一个线程已经持有了至少一个资源,但又提出了新的资源请求,而新的资源已被其他线程占用,所以当前线程会被阻塞,但阻塞的同时不释放自己获取的资源.
  • 不可剥夺条件
    获取到的资源在自己使用完之前不能被其他线程抢占,只能在使用完之后释放.
  • 环路等待条件
    发生死锁的时候必然存在一个线程-资源的环形链,即线程集合{T0,T1,T2,…Tn}中的T0正在等待一个T1占用的资源,T1正在等待T2占用的资源,…Tn正在等待T1占用的资源.

死锁示范

public class DeadLockTest {
	//创建资源
	private static Object resourceA = new Object();
	private static Object resourceB = new Object();
	
	public static void main(String[] args) {
		//创建线程
		Thread threadA = new Thread(new Runnable() {
			
			@Override
			public void run() {
				synchronized (resourceA) {
					System.out.println(Thread.currentThread() + " get ResourceA");
					
					try {
						Thread.sleep(1000);
					}
					catch (InterruptedException e) {
						e.printStackTrace();
					}
					
					System.out.println(Thread.currentThread() + " waiting get ResourceB");
					
					synchronized (resourceB) {
						System.out.println(Thread.currentThread() + " get ResourceB");
					}
				}
			}
		});
		
		Thread threadB = new Thread(new Runnable() {
			
			@Override
			public void run() {
				synchronized (resourceB) {
					System.out.println(Thread.currentThread() + " get ResourceB");
					
					try {
						Thread.sleep(1000);
					}
					catch (InterruptedException e) {
						e.printStackTrace();
					}
					
					System.out.println(Thread.currentThread() + " waiting get ResourceA");
					
					synchronized (resourceA) {
						System.out.println(Thread.currentThread() + " get ResourceA");
					}
				}
			}
		});
		
		//启动线程
		threadA.start();
		threadB.start();
	}
}

运行结果:

Thread[Thread-0,5,main] get ResourceA
Thread[Thread-1,5,main] get ResourceB
Thread[Thread-0,5,main] waiting get ResourceB
Thread[Thread-1,5,main] waiting get ResourceA

线程A持有资源A在等待资源B,线程B持有资源B在等待资源A,造成了死锁.

避免死锁的方法

造成死锁的4个条件只有:

  • 请求并持有条件
    指一个线程已经持有了至少一个资源,但又提出了新的资源请求,而新的资源已被其他线程占用,所以当前线程会被阻塞,但阻塞的同时不释放自己获取的资源.
  • 环路等待条件
    发生死锁的时候必然存在一个线程-资源的环形链,即线程集合{T0,T1,T2,…Tn}中的T0正在等待一个T1占用的资源,T1正在等待T2占用的资源,…Tn正在等待T1占用的资源.

上面2个是可以破坏的.

资源申请的有序性是可以破坏资源的请求并持有条件和环路等待条件.

public class DeadLockTest {
	//创建资源
	private static Object resourceA = new Object();
	private static Object resourceB = new Object();
	
	public static void main(String[] args) {
		//创建线程
		Thread threadA = new Thread(new Runnable() {
			
			@Override
			public void run() {
				synchronized (resourceA) {
					System.out.println(Thread.currentThread() + " get ResourceA");
					
					try {
						Thread.sleep(1000);
					}
					catch (InterruptedException e) {
						e.printStackTrace();
					}
					
					System.out.println(Thread.currentThread() + " waiting get ResourceB");
					
					synchronized (resourceB) {
						System.out.println(Thread.currentThread() + " get ResourceB");
					}
				}
			}
		});
		
		Thread threadB = new Thread(new Runnable() {
			
			@Override
			public void run() {
				synchronized (resourceA) {
					System.out.println(Thread.currentThread() + " get ResourceA");
					
					try {
						Thread.sleep(1000);
					}
					catch (InterruptedException e) {
						e.printStackTrace();
					}
					
					System.out.println(Thread.currentThread() + " waiting get ResourceB");
					
					synchronized (resourceB) {
						System.out.println(Thread.currentThread() + " get ResourceB");
					}
				}
			}
		});
		
		//启动线程
		threadA.start();
		threadB.start();
	}
}

程序输出:

Thread[Thread-0,5,main] get ResourceA
Thread[Thread-0,5,main] waiting get ResourceB
Thread[Thread-0,5,main] get ResourceB
Thread[Thread-1,5,main] get ResourceA
Thread[Thread-1,5,main] waiting get ResourceB
Thread[Thread-1,5,main] get ResourceB

锁的占用和释放

线程在sleep的时候只是不参与CPU时间的竞争,并不会释放线程本身持有的锁;

当前线程调用共享变量的wait()方法之后,只会释放当前共享变量上的锁,如果当前线程还持有其他共享变量的锁,则这些锁是不会被释放的。