加入CountDownLatch后死锁了
描述:打开某个界面后,就会A类的start方法,show方法中调用了B类的get方法,并用synchronized包裹,而在B类方法中使用了线程池来调用A类的showData方法,而调用的方式是通过A类提供的一个匿名内部类来调用。其中showData方法使用的synchronized关键字修饰,并且在方法里还使用了synchronized 包裹代码块,使用的锁对象与start方法的锁对象一样,是一个静态变量。
排除过程
- 观察showData有两个synchronized关键字,一个修饰方法,一个修饰代码块,我们知道方法不是static的所以锁的层次是实例级别,而修饰代码块的锁对象是static的,锁的层次是类级别。
- 首先得知道调用showData的对象是同一个吗?观察后得知showData是通过匿名内部类的方法调用的,然后这个内部类传递给了另一个类进行调用,我们知道synchronized修饰方法时,锁对象是this,那么在内部类中调用会怎么样?在showData使用this.hashCode方法打印后发现,hashcode与创建showData方法的对象一样,所以说锁对象还是同一个。
- 然后观察修饰的代码块的synchronized,同一个类中的start方法里也用到了同一锁对象,在方法调用过程中使用线程池执行了一个线程,使用countDownLatch来等待线程的结束。观察了一下线程池发现使用的拒绝策略,是当达到最大线程数时,由调用方执行的策略,但是synchronized是可重入锁,也不是线程池的问题。
- 观察线程池执行的线程会去调用showData,而showData方法又需要start方法释放锁,但是start方法又需要等待showdata方法执行完,这时就会死锁。
大概代码如下:
A:
public class Show {
private static Action action = new Action();
private static Object lock = new Object();
ShowInterface showInterface = () -> {
showData();
};;
public void start(){
synchronized (lock){
action.get();
}
}
private synchronized void showData(){
System.out.println("start doing");
synchronized (lock){
System.out.println("doing");
}
}
public ShowInterface getShowInterface() {
return showInterface;
}
}
B:
public class Action {
static Show show = new Show();
public void get(){
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 1,
TimeUnit.MINUTES, new LinkedBlockingQueue<>(10), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
CountDownLatch countDownLatch = new CountDownLatch(1);
threadPoolExecutor.execute(() ->{
try {
ShowInterface showInterface = show.getShowInterface();
showInterface.initData();
}finally {
countDownLatch.countDown();
}
});
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
输出:
start doing
结果可知,卡在了showData中,造成了死锁。
标签:一次,synchronized,经历,start,死锁,线程,new,showData,方法 From: https://www.cnblogs.com/theheboy/p/17206546.html