之前一直存在一个误区:当某一个线程进入临界区,如果它无法继续运行下去而陷入阻塞,是会自动释放掉自身持有的锁。因而,在Java中很难出现循环等待而导致死锁。结果证明这是错的,下面的代码出现了Synchronization关键字的嵌套使用,发生了死锁:
public class TestDeadLock1 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyThread5(0));
Thread t2 = new Thread(new MyThread5(1));
t1.start();
t2.start();
}
}
class MyThread5 implements Runnable{
private static Object o1 = new Object();
private static Object o2 = new Object();
private int flag;
public MyThread5(int flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag==0){
synchronized (o1) {
System.out.println(Thread.currentThread().getName()+"锁住了o1");
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println(Thread.currentThread().getName()+"执行结束");
}
}
}else{
synchronized (o2) {
System.out.println(Thread.currentThread().getName()+"锁住了o2");
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println(Thread.currentThread().getName()+"执行结束");
}
}
}
}
}
打印结果:
Thread-1锁住了o2
Thread-0锁住了o1
一个线程通过synchronize关键字获取了某个对象的锁,当他继续请求其他对象的锁而陷入阻塞时,并不会释放掉它当前所持有的锁。
如果是lock的嵌套使用呢:
package thread.deadlock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 死锁示例
* @author xiayunan
* @date 2018年7月14日
*
*/
public class TestDeadLock1 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyThread5(0));
Thread t2 = new Thread(new MyThread5(1));
t1.start();
t2.start();
}
}
class MyThread5 implements Runnable{
private static Lock l1 = new ReentrantLock();
private static Lock l2 = new ReentrantLock();
private int flag;
public MyThread5(int flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag==0){
l1.lock();
System.out.println(Thread.currentThread().getName()+"锁住了l1");
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
l2.lock();
System.out.println(Thread.currentThread().getName()+"执行结束");
l2.unlock();
l1.unlock();
}else{
l2.lock();
System.out.println(Thread.currentThread().getName()+"锁住了l2");
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
l1.lock();
System.out.println(Thread.currentThread().getName()+"执行结束");
l1.unlock();
l2.unlock();
}
}
}
打印出了类似的结果,可以得出类似的结论。
所以我们要在代码中,避免synchronize或者lock的嵌套使用。
现在来验证:如果一个线程获得了一个对象的锁,当因为某个条件无法再继续执行,通过调用wait()方法而陷入阻塞,它是否会释放掉它占有的锁?
/*
author:chxy
data:2020/3/23
description:
*/
package thread.deadlock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyThread1 implements Runnable{
private Lock lock = null;
private Condition condition = null;
MyThread1(Lock lock,Condition condition){
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
lock.lock();
System.out.println("thread1 has come in");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread1 has over");
lock.unlock();
}
}
class MyThread2 implements Runnable{
private Lock lock = null;
private Condition condition = null;
MyThread2(Lock lock,Condition condition){
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
lock.lock();
System.out.println("thread2 has come in,and i shall end first");
condition.signal();
lock.unlock();
}
}
public class TestDeadLock2 {
public static void main(String[] args) throws InterruptedException {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Thread t1 = new Thread(new MyThread1(lock,condition));
t1.start();
Thread.currentThread().sleep(1000);
Thread t2 = new Thread(new MyThread2(lock,condition));
t2.start();
}
}
打印的结果是:
thread1 has come in
thread2 has come in,and i shall end first
thread1 has over
证明:一个线程获得了一个对象的锁,当因为某个条件无法再继续执行,通过调用wait()方法而陷入阻塞,它是会释放掉它占有的锁。
坑:在synchronize代码块中无法使用wait和notify方法。只允许在synchronize方法中使用这两个方法,使线程因为等待某个条件而陷入阻塞或被因为条件满足而被唤醒。