无需等待即可锁定许多资源并保持 Java
Locking many resources without wait & hold in Java
我 运行 遇到了必须锁定 2 个或更多资源的问题,这导致在使用 Reent运行tReadWriteLocks 时出现死锁,即使在所有地方都具有相同的锁定顺序之后也是如此*。
我实现了一个获取锁定对象的方法,将它们全部锁定或回滚并抢占当前线程:
/**
* 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?
*代码应递归地实现概念 clustering/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
树节点来避免锁定和解锁多个 OS 互斥体(即 Lock
对象)的开销是 "locked," 并使用单个全局 Lock
对象来控制对 Set
.
的访问
我 运行 遇到了必须锁定 2 个或更多资源的问题,这导致在使用 Reent运行tReadWriteLocks 时出现死锁,即使在所有地方都具有相同的锁定顺序之后也是如此*。 我实现了一个获取锁定对象的方法,将它们全部锁定或回滚并抢占当前线程:
/**
* 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?
*代码应递归地实现概念 clustering/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
树节点来避免锁定和解锁多个 OS 互斥体(即 Lock
对象)的开销是 "locked," 并使用单个全局 Lock
对象来控制对 Set
.