我可以让线程等待锁,然后在释放后跳过它吗?

Can I make threads wait for a lock and then jump over it once released?

给出以下伪代码。一个函数可以同时被多个线程进入。我想让所有的线程都执行a()c(),但是b()必须只有那些在进入synchronized块时没有加锁的线程才能执行

换句话说:如果一个线程必须等待锁,那么我希望它等到锁被释放然后跳过 b() 并立即继续 c()

    public void code() {
        a() // check if page is present locally

        if (pageMissing) {
            synchronized (this) {
                page = b(); // get page over REST
            }
        }

        c() // access page
    }

这有什么用?想象一下 b() 调用外部 REST 函数来更新本地数据。当一个线程进入时,我们希望确保该函数被调用并且本地数据被更新,但是一旦阻塞线程退出 b() 我们知道本地数据是最新的并且我们不想浪费资源连续已经在等待的线程再次调用更新函数。

你可以有一个 boolean 标志(实例变量)来表示 b() 是否被调用。所有进入同步块的线程在调用b().

之前都会检查这个条件
private boolean isDone = false;

public void code() {
    a();
    synchronized (this) {
        if (!isDone) {
            b();
            isDone = true;
        }
    }
    c();
}

只有第一个获得锁的线程才会调用b()。成功后,它将标志设置为 true 并退出该块。所有进入该块的后续线程都将在 if 条件下失败,因此不会调用 b().


如果你不想让其他线程等待同步块的锁,想继续调用c(),你可以去掉synchronized关键字,使用AtomicBoolean并使用它的compareAndSet

 private AtomicBoolean isDone = new AtomicBoolean(false);

 public void code() {
    a();
    if (isDone.compareAndSet(false, true)) {
       b();
    }
    c();
}

compareAndSet 个状态的 Javadoc

Atomically sets the value to the given updated value if the current value {@code ==} the expected value.

@return {@code true} if successful. False return indicates that the actual value was not equal to the expected value.

因此,当 isDone 为 false 时第一个调用 compareAndSet 的线程将会成功。其他人将获得 false 作为 return 值,因此不会进入块,因此可以继续。


更新: 来自您的评论

@user7 Yes, I am concerned with threads calling b() unnecessarily, but (see my other comment) I need those threads that arrive while b() runs to wait until it is finished before they continue with c()

看来你需要我的第一个解决方案。