我遇到了必须锁定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)。锁定顺序始终是父节点,当前访问的节点,新节点。但是由于线程可能同时处于树的不同级别中,因此在某些时候,较高的子级和较低的树级中的父级之间会出现重叠,从而导致死锁。
使用Thread.yield()可以吗?还是Thread.sleep(1)更合适?
您应该注意,Thread.yield()
根本不能保证做任何事情。从有人想象Java程序可能在cooperative multitasking环境中运行的时代开始,这是过时的。协作多任务仍然存在,但是在功能强大到可以承载JVM的系统上,您通常不会找到它。
sleep(1)
调用is肯定会屈服,但这会影响程序的性能-这些天来很长的时间是毫秒。影响是否太大是一个只有您可以回答的问题。
我已经在Java代码中看到了sleep(0)
,但是我不知道这是否需要与yield()
有所不同。