2

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 或任何其他对象。

4

2 回答 2

2

我想不出一种简单的方法来使用高级 API 构建这种行为。请参阅下面使用等待/通知模式的建议。要点:

  • servicevolatile为了确保可见性而不必锁定阅读
  • service总是在本地复制,doSomething以防止您检查服务不为空,然后调用service.toString()并获取 NPE,因为setService(null);同时已被调用。
  • 在每个 while 循环中调整等待时间,以确保您等待的时间不超过 5 秒。
  • 它使用基本同步,但仅当 service 为 null 时 - 在 service 不为 null 的基本情况下,在doSomething. 如果您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());
    }
}
于 2013-02-04T14:45:29.790 回答
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);
    }
}
于 2013-02-04T13:45:00.987 回答