无需等待即可锁定许多资源并保持 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.

的访问