DependingService
取决于service
异步和动态注入DependingService.setService(Object)
的对象。如果在设置DependingService.doSomething()
对象之前调用service
,则线程应等待5秒钟以使service
可用。
如何正确有效地锁定?我的第一种方法是这样的:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class DependingService {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Condition condition = rwLock.writeLock().newCondition();
private Object service;
// service injected dynamically by container
public void setService(final Object service) {
rwLock.writeLock().lock();
try {
this.service = service;
System.out.println("Signalling");
condition.signalAll();
} finally {
rwLock.writeLock().unlock();
}
}
public void doSomething() {
rwLock.readLock().lock();
try {
if (service == null) {
// we can't upgrade to write lock, so release read lock first
rwLock.readLock().unlock();
rwLock.writeLock().lock();
try {
if (service == null) {
System.out.println("Waiting fo 5 seconds");
condition.await(5, TimeUnit.SECONDS);
}
} catch (final InterruptedException e) {
e.printStackTrace();
} finally {
// downgrade to read lock
rwLock.readLock().lock();
rwLock.writeLock().unlock();
}
if (service == null) {
throw new RuntimeException("service is null");
}
}
// use the service
System.out.println(service.toString());
} finally {
rwLock.readLock().unlock();
}
}
}
修改
请注意,DependingService.setService(Object)
可以是任意时间,多次设置为null或任何其他对象。
答案 0 :(得分:2)
我想不出用高级API构建这种行为的简单方法。请参阅下面的使用等待/通知模式的建议。要点:
service
为volatile
以确保可见性而无需锁定阅读service
中本地复制doSomething
,以防止您检查服务不为空的情况,然后调用service.toString()
并获取NPE,因为已调用setService(null);
在此期间。doSomething
中没有争用。如果每隔ms调用setService
多次,则可能会遇到性能问题。注意:未经测试。
public class DependingService {
private final Object lock = new Object();
private volatile Object service;
// service injected dynamically by container
public void setService(final Object service) {
this.service = service;
synchronized(lock) {
lock.notifyAll();
}
}
public void doSomething() throws InterruptedException {
//make a local copy to avoid problems due to service becoming
//null in the middle of the method
Object localService = service;
if (localService == null ) {
long end = System.nanoTime() + TimeUnit.NANOSECONDS.convert(5, TimeUnit.SECONDS);
synchronized(lock) {
while ((localService = service) == null) {
long waitNanos = end - System.nanoTime();
if (waitNanos < 0) break;
lock.wait(waitNanos / 1000000);
}
}
}
if (localService == null) {
throw new RuntimeException("timeout: service is still null");
}
// use the service
System.out.println(localService.toString());
}
}
答案 1 :(得分:0)
使用CountDownLatch可能更好:
public class DependingService {
private final CountDownLatch serviceLatch = new CountDownLatch (1);
private Object service;
public void setService (final Object service)
{
this.service = service;
serviceLatch.countDown ();
}
public void doSomething () throws InterruptedException
{
if (!serviceLatch.await (5, TimeUnit.SECONDS))
throw new RuntimeException ("Service is still null");
// Service is not null here
System.out.println (service);
}
}