如果 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();
}
换句话说,暂时抓住锁然后释放它。
但这就是问题所在。
一般来说,在你释放锁的那一刻,其他线程可能会立即抢走它。因此,您的 array.add()
调用可能会与其他线程对 array
执行操作同时发生。即使您的 use-case 意味着另一个线程获取锁的可能性很小,它仍然可能发生;例如如果你的服务器负载过重,当前线程在释放锁后立即被抢占。
推测您正在 array.add()
中执行内存写入。除非以适当的同步方式执行,否则这些更新可能对其他线程不可见。 (你说 "addToArray method is threadproof",但如果没有清楚、详细地解释你的意思,我会很不自在地说 this代码是线程安全的。)
如果您在这里尝试做的是 array.add()
在发生其他事情之后,那么测试锁/等待它被释放不会告诉您事件是否发生实际上发生了。它告诉你的只是它并没有在测试成功的那一刻发生。
简而言之,我怀疑在进行更新之前等待释放锁实际上是一个正确的解决方案......无论您如何实现等待。
另一种看待这个问题的方式。
如果 array.add()
是完全线程安全的,并且无论其他线程是否持有锁都能正常工作,为什么 需要 来测试锁?直接调用方法即可。
如果您实际上试图让 array.add()
调用发生在某个与释放锁同时发生的事件之后,请使用循环屏障或类似方法。
注意:我阅读并试图理解您的解释,但我对您所说的内容感到困惑。由于 "language issues" 我认为。
据我了解,您有两个或多个单独的线程改变列表:主线程偶尔会清除列表,而 netty 线程会添加到列表中。您要确保它们不会同时尝试修改列表。
最简单的解决方案是使用线程安全列表,并确保主线程使用 List.clear() 方法清除列表。这样,clear() 调用将是原子的 - 一旦启动,它将在任何其他访问列表之前完成 - 因此您不必担心将 clear() 调用添加到列表中"in the middle of"。
在对另一个答案的评论中,您提到您正在使用线程安全的 CopyOnWriteArrayList。因此,您可以只调用 add() 添加到列表中的代码,而不必担心同步问题;如果正在清除列表,则 add() 调用将自动等待,否则继续。您也可以从主线程中删除对 ReentrantLock 的使用,除非除了保护此列表之外还有其他原因需要使用锁。
我的代码中有一个 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();
}
换句话说,暂时抓住锁然后释放它。
但这就是问题所在。
一般来说,在你释放锁的那一刻,其他线程可能会立即抢走它。因此,您的
array.add()
调用可能会与其他线程对array
执行操作同时发生。即使您的 use-case 意味着另一个线程获取锁的可能性很小,它仍然可能发生;例如如果你的服务器负载过重,当前线程在释放锁后立即被抢占。推测您正在
array.add()
中执行内存写入。除非以适当的同步方式执行,否则这些更新可能对其他线程不可见。 (你说 "addToArray method is threadproof",但如果没有清楚、详细地解释你的意思,我会很不自在地说 this代码是线程安全的。)如果您在这里尝试做的是
array.add()
在发生其他事情之后,那么测试锁/等待它被释放不会告诉您事件是否发生实际上发生了。它告诉你的只是它并没有在测试成功的那一刻发生。
简而言之,我怀疑在进行更新之前等待释放锁实际上是一个正确的解决方案......无论您如何实现等待。
另一种看待这个问题的方式。
如果
array.add()
是完全线程安全的,并且无论其他线程是否持有锁都能正常工作,为什么 需要 来测试锁?直接调用方法即可。如果您实际上试图让
array.add()
调用发生在某个与释放锁同时发生的事件之后,请使用循环屏障或类似方法。
注意:我阅读并试图理解您的解释,但我对您所说的内容感到困惑。由于 "language issues" 我认为。
据我了解,您有两个或多个单独的线程改变列表:主线程偶尔会清除列表,而 netty 线程会添加到列表中。您要确保它们不会同时尝试修改列表。
最简单的解决方案是使用线程安全列表,并确保主线程使用 List.clear() 方法清除列表。这样,clear() 调用将是原子的 - 一旦启动,它将在任何其他访问列表之前完成 - 因此您不必担心将 clear() 调用添加到列表中"in the middle of"。
在对另一个答案的评论中,您提到您正在使用线程安全的 CopyOnWriteArrayList。因此,您可以只调用 add() 添加到列表中的代码,而不必担心同步问题;如果正在清除列表,则 add() 调用将自动等待,否则继续。您也可以从主线程中删除对 ReentrantLock 的使用,除非除了保护此列表之外还有其他原因需要使用锁。