【问题标题】:Maintain thread safety while preventing deadlock from possibly synchronous callback保持线程安全,同时防止可能的同步回调死锁
【发布时间】:2018-01-05 01:56:01
【问题描述】:

我有一个类似下面的 API,其中 Baz 是工作人员实现。 这个Bar 需要是线程安全的,这在与 Baz 的回调交互时会变得很棘手。

需要在回调中引用当前的 baz 实例(可以在工作线程上调用或同步调用)。 cmets 应该显示问题:

final class Bar {
  final Lock lock = new ReentrantLock();
  Baz baz; // Guarded by lock.

  void run() { // Called by any thread.
    lock.lock();
    if (baz.isRunning()) {
      lock.unlock();
      return;
    }
    baz = new Baz();
    // If it unlocks here, the next line may execute on the wrong Baz.
    // If it doesn't unlock here, there will be a deadlock when done() is called synchronously.
    // lock.unlock();
    baz.run(new Baz.Callback() { // May be called synchronously or by Baz worker thread.
      @Override
      public void done() {
        lock.lock();
        baz = new Baz();
        lock.unlock();
      }
    });
  }
}

有没有一种好方法可以使这项工作正常工作,同时又不会导致死锁?

编辑:更简洁:

final class Foo {
  final Lock lock = new ReentrantLock();

  void run() {
    lock.lock();
    worker.enqueue(new Callback() {
      @Override void complete() {
        lock.lock(); // Could cause deadlock.
      }
    });
    lock.unlock();
  }
}

【问题讨论】:

    标签: java multithreading asynchronous thread-safety synchronous


    【解决方案1】:

    不确定是否完全得到您想要实现的目标,但也许这就是您想要的?

    final class Bar {
        final Lock lock = new ReentrantLock();
        Baz baz = new Baz();
    
        void run() {
            if (!lock.tryLock()) {
                return;
            }
            try {
                CountdownLatch callbackFlag = new CountdownLatch(1);
                baz.run(new Baz.Callback() {
                    @Override
                    public void done() {
                        callbackFlag.countDown();
                    }
                });
                try {
                    callbackFlag.await(); // better use overloaded method with max timeout waiting. you don't probably want to wait forever
                    baz = new Baz(); // do you really want to reinit Baz on each execution?
                } catch (InterruptedException e) {
                    // decide what you want to happen here
                }
            } finally {
                lock.unlock();
            }
        }
    }
    

    【讨论】:

    • 不过,baz 分配需要在回调中。
    • 为什么要在回调中引用?我发布的代码确保在回调完成之前 baz 不会重新初始化
    • 无论如何,如果你真的需要,你可以把那行代码移到回调中;无论如何,一切都会好起来的,线程安全也会好的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-14
    • 2016-04-01
    • 2020-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多