首页 > 其他分享 >一次死锁经历

一次死锁经历

时间:2023-03-11 17:33:05浏览次数:37  
标签:一次 synchronized 经历 start 死锁 线程 new showData 方法

加入CountDownLatch后死锁了

描述:打开某个界面后,就会A类的start方法,show方法中调用了B类的get方法,并用synchronized包裹,而在B类方法中使用了线程池来调用A类的showData方法,而调用的方式是通过A类提供的一个匿名内部类来调用。其中showData方法使用的synchronized关键字修饰,并且在方法里还使用了synchronized 包裹代码块,使用的锁对象与start方法的锁对象一样,是一个静态变量。

排除过程

  1. 观察showData有两个synchronized关键字,一个修饰方法,一个修饰代码块,我们知道方法不是static的所以锁的层次是实例级别,而修饰代码块的锁对象是static的,锁的层次是类级别。
  2. 首先得知道调用showData的对象是同一个吗?观察后得知showData是通过匿名内部类的方法调用的,然后这个内部类传递给了另一个类进行调用,我们知道synchronized修饰方法时,锁对象是this,那么在内部类中调用会怎么样?在showData使用this.hashCode方法打印后发现,hashcode与创建showData方法的对象一样,所以说锁对象还是同一个。
  3. 然后观察修饰的代码块的synchronized,同一个类中的start方法里也用到了同一锁对象,在方法调用过程中使用线程池执行了一个线程,使用countDownLatch来等待线程的结束。观察了一下线程池发现使用的拒绝策略,是当达到最大线程数时,由调用方执行的策略,但是synchronized是可重入锁,也不是线程池的问题。
  4. 观察线程池执行的线程会去调用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

相关文章