Java: Object.wait(长) 坏了
Java: Object.wait(long) broken
在我看来,Object.wait 的超时版本很少按原样使用。这是因为:
- 该方法不处理虚假唤醒。
- 该方法不会指示它是由于通知还是超时而返回。
以谓词为参数的C++ version似乎是对的。带有签名
的对象中对应的 Java 方法
boolean wait(long timeOutMillis, BooleanSupplier condition)
可以方便的使用如下:
Object obj = ...;
BooleanSupplier condition = ...;
synchronized (obj) {
if (obj.wait(1000, condition)) {
// condition is fulfilled
} else {
// timeout happened
}
}
作为解决方法,我可以使用以下丑陋的辅助方法:
public static boolean safeWait(Object waitObject, long timeOutMillis, BooleanSupplier condition) throws InterruptedException {
if (condition.getAsBoolean()) {
return true;
}
long rest = timeOutMillis;
while (true) {
long t0 = System.currentTimeMillis();
waitObject.wait(rest);
long t1 = System.currentTimeMillis();
long waited = t1 - t0;
if (condition.getAsBoolean()) {
return true;
}
rest = rest - waited;
if (rest <= 0) {
return false;
}
}
}
我最好提出一些问题:
- 我说得对吗,它坏了?
- 他们为什么不解决这个问题?
- 有没有更好的方法来解决这个问题?
你的safeWait(...)
原则上看起来不错,但你可以稍微精简一下:
public static boolean safeWait(Object waitObject, long timeOutMillis, BooleanSupplier condition)
throws InterruptedException
{
long now = System.currentTimeMillis();
long end_time = now + timeOutMillis;
while (! condition.getAsBoolean()) {
if (now > end_time) {
return false;
}
waitObject.wait(end_time - now);
now = System.currentTimeMillis();
}
return true;
}
Object.wait() 有一些限制,这就是 Java 引入 Lock 的原因。并且通过使用 "trylock()" 方法,如果锁已被其他线程获取,您的代码不会阻塞。
见下文:
Boolean workDone = false;
while (!workDone) {
Lock lock = new ReentrantLock();
Boolean lockAcquired = false;
try {
lockAcquired = lock.tryLock(5, TimeUnit.SECONDS);
} catch (InterruptedException e) { // this exception will be thrown if current thread is interrupted while acquiring the lock or has its interrupted status set on entry to "tryLock" method.
Thread.currentThread().interrupt(); // this thread's interrupt status will be set to "true" (this is needed because the current thread's interrupted status was cleared by InterruptedException)
System.out.println(Thread.currentThread().isInterrupted()); // true
}
if (Thread.currentThread().isInterrupted()) {
// close resources. finish as quick as possible
}
if (lockAcquired) {
// you have the lock. you can execute the critical section of the code (read/modify mutable shared state)
lock.unlock(); // remember to release the lock at the end of your critical section.
workDone = true;
}
}
在我看来,Object.wait 的超时版本很少按原样使用。这是因为:
- 该方法不处理虚假唤醒。
- 该方法不会指示它是由于通知还是超时而返回。
以谓词为参数的C++ version似乎是对的。带有签名
的对象中对应的 Java 方法boolean wait(long timeOutMillis, BooleanSupplier condition)
可以方便的使用如下:
Object obj = ...;
BooleanSupplier condition = ...;
synchronized (obj) {
if (obj.wait(1000, condition)) {
// condition is fulfilled
} else {
// timeout happened
}
}
作为解决方法,我可以使用以下丑陋的辅助方法:
public static boolean safeWait(Object waitObject, long timeOutMillis, BooleanSupplier condition) throws InterruptedException {
if (condition.getAsBoolean()) {
return true;
}
long rest = timeOutMillis;
while (true) {
long t0 = System.currentTimeMillis();
waitObject.wait(rest);
long t1 = System.currentTimeMillis();
long waited = t1 - t0;
if (condition.getAsBoolean()) {
return true;
}
rest = rest - waited;
if (rest <= 0) {
return false;
}
}
}
我最好提出一些问题:
- 我说得对吗,它坏了?
- 他们为什么不解决这个问题?
- 有没有更好的方法来解决这个问题?
你的safeWait(...)
原则上看起来不错,但你可以稍微精简一下:
public static boolean safeWait(Object waitObject, long timeOutMillis, BooleanSupplier condition)
throws InterruptedException
{
long now = System.currentTimeMillis();
long end_time = now + timeOutMillis;
while (! condition.getAsBoolean()) {
if (now > end_time) {
return false;
}
waitObject.wait(end_time - now);
now = System.currentTimeMillis();
}
return true;
}
Object.wait() 有一些限制,这就是 Java 引入 Lock 的原因。并且通过使用 "trylock()" 方法,如果锁已被其他线程获取,您的代码不会阻塞。
见下文:
Boolean workDone = false;
while (!workDone) {
Lock lock = new ReentrantLock();
Boolean lockAcquired = false;
try {
lockAcquired = lock.tryLock(5, TimeUnit.SECONDS);
} catch (InterruptedException e) { // this exception will be thrown if current thread is interrupted while acquiring the lock or has its interrupted status set on entry to "tryLock" method.
Thread.currentThread().interrupt(); // this thread's interrupt status will be set to "true" (this is needed because the current thread's interrupted status was cleared by InterruptedException)
System.out.println(Thread.currentThread().isInterrupted()); // true
}
if (Thread.currentThread().isInterrupted()) {
// close resources. finish as quick as possible
}
if (lockAcquired) {
// you have the lock. you can execute the critical section of the code (read/modify mutable shared state)
lock.unlock(); // remember to release the lock at the end of your critical section.
workDone = true;
}
}