用于超时参数的java.util.concurrent.TimeUnit vs java.time.Duration

ant*_*tak 5 java java-8

在编写API时,我倾向于定义将超时参数作为参数的方法long timeout, TimeUnit unit.这遵循许多java.util.concurrent具有相同要求的方法.

在Java 8中,我注意到了包中的新Durationjava.time,并想知道我是否可以将新方法定义为具有Duration timeout参数.

我可以从中看到的优势是:

但这是一个可能的问题:

有没有更好的方法呢?java.time.Duration在我提供的API中是否存在其他问题或原因我不应该用于超时参数?

Hol*_*ger 8

你的代码

private static final long ACQUIRE_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(10);
someResource.acquire(ACQUIRE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
Run Code Online (Sandbox Code Playgroud)

抵消了在特定应用单元中与该单元一起提供价值的预期用途.预期用途应该是someResource.acquire(10, TimeUnit.MINUTES);

但它显示了这种方法的问题,因为不可能定义一个带有值和a的常量TimeUnit.所以Duration在这里有一点意义.

由于TimeUnit在大多数情况下是为并发API创建并由后端代码使用的众所周知的类型,我永远不会省略带(long,TimeUnit)签名的方法,但提供接受a的重载Duration并没有错.


当涉及到"无超时"问题时,大多数API都没有特殊处理Long.MAX_VALUE,无论它是以秒还是毫秒提供.观察到的"无超时"行为是由于这两个数量(Long.MAX_VALUE, TimeUnit.SECONDS)并且(Long.MAX_VALUE, TimeUnit.MILLISECONDS)表示时间这一事实,今天的计算机都无法存活.如果人类持续那么久,我们会很高兴.使用Long.MAX_VALUE-1将表现出相同的行为.

顺便说一下,一些并发工具无条件地在内部将超时转换为纳秒级,这意味着最大等待时间仅限于300年"仅",但我认为对于大多数仍然等于"几乎没有超时"的应用程序.

换句话说,在运行时操作中用作超时之间Duration.ofSeconds(Long.MAX_VALUE)和之间没有实际区别Long.MAX_VALUE, TimeUnit.MILLISECONDS.


附录:我首先忽略了关于实际实施的问题.我建议遵循上述转换为纳秒的行为,而不是使用毫秒.我能想到的最好的事情是

public boolean acquire(long timeout, TimeUnit timeUnit) {
    // actual implementation
}
static final Duration MAX_WAIT = Duration.ofNanos(Long.MAX_VALUE);
public boolean acquire(Duration timeout) {
    return acquire(
        timeout.compareTo(MAX_WAIT)>=0? Long.MAX_VALUE: timeout.toNanos(),
        TimeUnit.NANOSECONDS);
}
Run Code Online (Sandbox Code Playgroud)

有可能用毫秒来做类似的事情,但是,我认为将来毫秒级的毫秒不精确度更可能成为问题,而最大超时限制在292年以上.

  • 在 Java 11+ 中,您可以使用 [`TimeUnit#convert(Duration)`](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/concurrent/ TimeUnit.html#convert(java.time.Duration)) 在溢出的情况下不会抛出 `ArithmeticException`,并根据需要选择返回 `Long.MIN_VALUE` 或 `Long.MAX_VALUE`。@安塔克 (4认同)