我遇到了必须锁定 2 个或更多资源的问题,这导致在使用 ReentrantReadWriteLocks 时出现死锁,即使在各处都有相同的锁定顺序*。 我实现了一种方法,该方法采用锁定对象,锁定所有对象或回滚并抢占当前线程:
/**
* Helper Interface for an AutoClosable lock.
*/
public interface ResourceLock extends AutoCloseable {
/**
* Unlocking doesn't throw any checked exception.
*/
@Override
void close();
}
public static ResourceLock lockAll(Lock... locks) {
List<Lock> successful = new ArrayList<>();
boolean acquired = false;
for (final Lock lock : locks) {
acquired = false;
try {
acquired = lock.tryLock(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (acquired) {
successful.add(lock);
} else {
break;
}
}
if (!acquired) {
for (Lock lock1 : successful) {
lock1.unlock();
}
// Preempt the thread and try again
Thread.yield();
return lockAll(locks);
}
return () -> {
for (final Lock lock : locks) {
lock.unlock();
}
};
}
使用示例:
try (ResourceLock ignored = lockAll(currentNode.getLock().writeLock(), newNode.getLock().readLock())) {
currentNode.updateCounts(newNode);
}
不太好读。
这是我的问题:
- 如何在 Java 中正确抢占线程?
- 使用 Thread.yield() 可以吗,还是 Thread.sleep(1) 更合适?
——还有比这更优雅的事吗?例如我是否监督了做这件事或某事的最佳实践?在 util.concurrency 中?
*代码应递归地实现概念聚类/Cobweb(Fisher,1987)的多线程版本。锁定顺序始终是父节点、当前访问节点、新节点。但由于线程可能同时位于树的不同级别,因此在某些时候,较高树级别中的子级和较低树级别中的父级之间存在重叠,从而导致死锁。
最佳答案
Is the use of Thread.yield() okay? or would Thread.sleep(1) be more appropriate?
您应该知道 Thread.yield()
根本不能保证执行任何操作。当有人想象 Java 程序可能在 cooperative multitasking 中运行时,这是不合时宜的。环境。协作式多任务处理仍然存在,但您通常不会在功能强大到足以托管 JVM 的系统上找到它。
sleep(1)
调用保证会产生结果,但它会影响程序的性能——如今,一毫秒已经很长了。影响是否太大,这个问题只有你自己才能回答。
我在 Java 代码中见过 sleep(0)
,但我不知道这是否需要与 yield()
表现出任何不同。
Is there something more elegant than this?
也许不是更优雅,但您可以通过保留树节点的全局Set
来避免锁定和解锁多个操作系统互斥体(即Lock
对象)的开销被“锁定”,并使用单个全局 Lock
对象来控制对 Set
的访问。
关于java - 在Java中无需等待和保持即可锁定许多资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58908006/