线程死锁

先简单用生活列子介绍死锁,

         2个人一起吃饭但是只有一双筷子,2人轮流吃(同时拥有2只筷子才能吃)。某一个时候,一个拿了左筷子,一人拿了右筷子,2个人都同时占用一个资源,等待另一个资源,这个时候甲在等待乙吃完并释放它占有的筷子,同理,乙也在等待甲吃完并释放它占有的筷子,这样就陷入了一个死循环,谁也无法继续吃饭。。

                                         总而言之

当前线程不释放资源

当前线程拥有其他线程执行的资源

当前线程等待执行其他线程的资源

 

锁顺序死锁

  • 线程A调用method1()方法,得到obj1锁

  • 同时线程B调用methd2()方法,得到obj2锁

  • 线程A和线程B都继续执行,此时线程A需要obj2锁才能继续往下执行。此时线程B需要obj1锁才能继续往下执行。

  • 但是:线程A的obj1锁并没有释放,线程B的obj2锁也没有释放

  • 所以他们都只能等待,而这种等待是无期限的-->永久等待-->死锁

/**
 * 
 */
package demo3;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author liuchaojun
 * @date 2018-12-14 上午11:02:50
 */
public class Lock1 {
 
    
    public static void main(String[] args) {
    	Object obj1 = new Object();
    	Object obj2 = new Object();
    	ExecutorService e = Executors.newFixedThreadPool(5);
    	Task1 t =  new Task1(obj1, obj2);
    	for (int i = 0; i < 10; i++) {
			e.execute(t);
		}
	}
}
class Task1 implements Runnable{
	private Object obj1;
	private Object obj2;
	
	/**
	 * @param obj1
	 * @param obj2
	 */
	public Task1(Object obj1, Object obj2) {
		super();
		this.obj1 = obj1;
		this.obj2 = obj2;
	}

	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		  // 得到obj1锁
        synchronized (obj1) {
        	try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
            // 得到obj2锁
            synchronized (obj2) {
                System.out.println("111111111");
            }
        }
		   // 得到obj2锁
        synchronized (obj2) {
        	try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
            // 得到obj1锁
            synchronized (obj1) {
                System.out.println("2222222222222");
            }
        }
	}
	
}
 
 

第二种:动态锁顺序死锁

  • 线程A的用户accout转账给fromAccount

  • 线程B的用户fromAccount转账给accout用户

  • 如果两个线程同时进行调用method1方法

  • 那么就会发生死锁。

/**
 * 
 */
package demo3;

/**
 * @author liuchaojun
 * @date 2018-12-14 上午11:02:50
 */
public class Lock2 {

	void method1(Object account, Object fromAccount) {
		synchronized (account) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			// 得到obj2锁
			synchronized (fromAccount) {
				System.out.println(account+"给"+fromAccount+"转账");
			}
		}
	}

	public static void main(String[] args) {
		final Lock2 lock2 = new Lock2();
		final Object obj1 = new Object();
		final Object obj2 = new Object();
		new Thread(new Runnable() {
			@Override
			public void run() {
				lock2.aa(obj1, obj2);
			}
		}).start();
		new Thread(new Runnable() {

			@Override
			public void run() {
				lock2.aa(obj2, obj1);
			}
		}).start();
	}
}

二、避免死锁的方法

避免死锁可以概括成三种方法:

  • 固定加锁的顺序(针对锁顺序死锁)

  • 开放调用(针对对象之间协作造成的死锁)

  • 使用定时锁-->tryLock()

         如果等待获取锁时间超时,则抛出异常而不是一直等待

 

         如上面转账代码,我们根据hash值进行上锁,固定加锁的顺序,这样就可以解决锁顺序死锁的问题

/**
 * 
 */
package demo3;

/**
 * @author liuchaojun
 * @date 2018-12-14 上午11:02:50
 */
public class Lock2 {
	private static final Object newObj = new Object();

	void method1(Object account, Object fromAccount) {
		// 得到锁的hash值
		int accountHash = System.identityHashCode(account);
		int fromAccountHash = System.identityHashCode(fromAccount);
		if (accountHash > fromAccountHash) {
			synchronized (account) {
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				// 得到obj2锁
				synchronized (fromAccount) {
					System.out.println(account + "给" + fromAccount + "转账");
				}
			}
		} else if (accountHash < fromAccountHash) {
			synchronized (fromAccount) {
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				// 得到obj2锁
				synchronized (account) {
					System.out.println(account + "给" + fromAccount + "转账");
				}
			}
		} else {
			synchronized (newObj) {
				synchronized (fromAccount) {
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					// 得到obj2锁
					synchronized (account) {
						System.out.println(account + "给" + fromAccount + "转账");
					}
				}
			}
		}

	}

	public static void main(String[] args) {
		final Lock2 lock2 = new Lock2();
		final Object obj1 = new Object();
		final Object obj2 = new Object();
		new Thread(new Runnable() {
			@Override
			public void run() {
				lock2.method1(obj1, obj2);
			}
		}).start();
		new Thread(new Runnable() {

			@Override
			public void run() {
				lock2.method1(obj2, obj1);
			}
		}).start();
	}
}

使用定时锁

使用显式Lock锁,在获取锁时使用tryLock()方法。当等待超过时限的时候,tryLock()不会一直等待,而是返回错误信息。

使用tryLock()能够有效避免死锁问题~~线程死锁_第1张图片

2.4死锁检测

虽然造成死锁的原因是因为我们设计得不够好,但是可能写代码的时候不知道哪里发生了死锁。

JDK提供了两种方式来给我们检测:

  1. JconsoleJDK自带的图形化界面工具,使用JDK给我们的的工具JConsole

  2. Jstack是JDK自带的命令行工具,主要用于线程Dump分析。

 

你可能感兴趣的:(多线程,java)