如果 ReentrantLock 被锁定等待但不锁定锁

if ReentrantLock is locked wait but dont lock the lock

我的代码中有一个 ReentrantLock,我想用它每秒清除一次数组;我不希望其他线程在清除数组时更改数组,但如果我当前没有清除数组,则其他线程不必等待,如下所示:

public void addToArray(Object a) {
    lock.waitforunlock(); //not a real method just to clarify my intentions
    array.add(a);
}

为了更好地阐明我的意图,我将解释该过程:netty eventloop 将调用我的网络处理程序,然后该网络处理程序将调用之前的 addToArray 方法,每秒一次我的主线程永远不会是 Netty 线程将清除数组,此时每个 netty 线程都必须等到完成!注意:addToArray 方法是线程安全的,我不想同步它,因为那样事件循环的空洞点就没用了。

没有 API 方法可以完全满足您的要求。

最有效的方法是这样的:

try {
    lock.lock();
} finally {
    lock.unlock();
}

换句话说,暂时抓住锁然后释放它。

但这就是问题所在。

  1. 一般来说,在你释放锁的那一刻,其他线程可能会立即抢走它。因此,您的 array.add() 调用可能会与其他线程对 array 执行操作同时发生。即使您的 use-case 意味着另一个线程获取锁的可能性很小,它仍然可能发生;例如如果你的服务器负载过重,当前线程在释放锁后立即被抢占。

  2. 推测您正在 array.add() 中执行内存写入。除非以适当的同步方式执行,否则这些更新可能对其他线程不可见。 (你说 "addToArray method is threadproof",但如果没有清楚、详细地解释你的意思,我会很不自在地说 this代码是线程安全的。)

  3. 如果您在这里尝试做的是 array.add() 在发生其他事情之后,那么测试锁/等待它被释放不会告诉您事件是否发生实际上发生了。它告诉你的只是它并没有在测试成功的那一刻发生。

简而言之,我怀疑在进行更新之前等待释放锁实际上是一个正确的解决方案......无论您如何实现等待。


另一种看待这个问题的方式。

  • 如果 array.add() 是完全线程安全的,并且无论其他线程是否持有锁都能正常工作,为什么 需要 来测试锁?直接调用方法即可。

  • 如果您实际上试图让 array.add() 调用发生在某个与释放锁同时发生的事件之后,请使用循环屏障或类似方法。


注意:我阅读并试图理解您的解释,但我对您所说的内容感到困惑。由于 "language issues" 我认为。

据我了解,您有两个或多个单独的线程改变列表:主线程偶尔会清除列表,而 netty 线程会添加到列表中。您要确保它们不会同时尝试修改列表。

最简单的解决方案是使用线程安全列表,并确保主线程使用 List.clear() 方法清除列表。这样,clear() 调用将是原子的 - 一旦启动,它将在任何其他访问列表之前完成 - 因此您不必担心将 clear() 调用添加到列表中"in the middle of"。

在对另一个答案的评论中,您提到您正在使用线程安全的 CopyOnWriteArrayList。因此,您可以只调用 add() 添加到列表中的代码,而不必担心同步问题;如果正在清除列表,则 add() 调用将自动等待,否则继续。您也可以从主线程中删除对 ReentrantLock 的使用,除非除了保护此列表之外还有其他原因需要使用锁。