死锁是两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候。
例如,如果线程1锁住了A,然后尝试对B进行加锁,同时线程2已经锁住了B,接着尝试对A进行加锁,这时死锁就发生了。线程1永远得不到B,线程2也永远得不到A,并且它们永远也不会知道发生了这样的事情。为了得到彼此的对象(A和B),它们将永远阻塞下去。这种情况就是一个死锁。
该情况如下:
Thread 1 locks A, waits for B
Thread 2 locks B, waits for A
package com.smart.concurrency.chapter1;
/**
* @Description 死锁的例子
* @author gaowenming
*/
public class DeadLock {
/** A锁 */
private static String A = "A";
/** B锁 */
private static String B = "B";
public static void main(String[] args) {
new DeadLock().deadLock();
}
public void deadLock() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (A) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println("thread1...");
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (B) {
synchronized (A) {
System.out.println("thread2...");
}
}
}
});
t1.start();
t2.start();
}
}
上面的2个线程的执行过程如下:
线程1开始执行,拿到了锁A,休眠2秒,线程2执行,先拿到了锁B,在请求拿锁A,而由于此时线程1已经占用了锁A,所以就等待线程1释放锁A,休眠结束后,线程1继续往下执行,请求拿到锁B,而锁B被线程2占用,从而形成了死锁。
java中,解决死锁一般有如下方法:
1)尽量使用tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。
2)尽量使用java.util.concurrent(jdk 1.5以上)包的并发类代替手写控制并发,比较常用的是ConcurrentHashMap、ConcurrentLinkedQueue、AtomicBoolean等等,实际应用中java.util.concurrent.atomic十分有用,简单方便且效率比使用Lock更高
3)尽量降低锁的使用粒度,尽量不要几个功能用同一把锁
4)尽量减少同步的代码块