仅锁定在单个入口点

Locking only on a single entry point

我正在使用 ExcutorService 并行向 运行 提交任务。任务的顺序无关紧要。然而,服务可能会改变,特别是当请求的池大小需要改变时。

public class Service {

    private volatile ExecutorService service = Executors.newFixedThreadPool(4);

    private final ReentrantLock serviceLock = new ReentrantLock();

    public Future<Object> postRequest(final Callable<Object> request) {
        try {
            serviceLock.lock(); // ?
            return service.submit(request);
        } finally {
            serviceLock.unlock(); // ?
        }
    }

    public void setSize(final int size) {
        try {
            if (size <= 0) {
                throw new IllegalArgumentException("service pool size must positive");
            }
            serviceLock.lock();

            service = Executors.newFixedThreadPool(size);
        } finally {
            serviceLock.unlock();
        }
    }
}

显然,我认为我不需要 postRequest 方法中的锁定和解锁,因为它是唯一被调用的方法。

我只需要在访问 setSize 期间锁定 postRequest。否则,锁定和解锁所需的额外时间毫无意义。我认为这是必要的,因为与提交的数百个请求相比,大小很少会发生变化(可能一次或两次)。

有没有办法避免在不需要时(当 setSize 未被访问时)锁定 postRequest

由于 Executor 实际上是一个 ThreadPoolExecutor,您可以应用强制转换并使用其 setMaximumPoolSize 方法。

您可以在 setSize 方法中添加类型检查,以防将来更新 JVM 规范。

    if (e instanceof ThreadPoolExecutor) {
        ((ThreadPoolExecutor) e).setMaximumPoolSize(size);
    }

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html#setMaximumPoolSize(int)

@Obicere,你是对的,你不需要锁定 postRequest 这会产生性能开销。 在 postRequest

中使用类似的东西
  public Future<Object> postRequest(final Callable<Object> request) {
        try {
            while(serviceLock.isLocked()) { // i.e acquired by setSize method
                Thread.currentThread().sleep(/*some very small time */); 
            }
            return service.submit(request);
        } finally {

        }
    }

此外,请考虑使用 ThreadPoolExecutor 而不是 FixedPoolThread,因为在您的情况下,它从来都不是固定池线程:)

您可以显式创建一个线程池执行器,然后使用 setMaximumPoolSize():

private final ThreadPoolExecutor service = new ThreadPoolExecutor(4,4,0, TimeUnit.MINUTE, new LinkedBlockingQueue());


public void setSize(final int size) {
 service.setMaximumPoolSize(size);
}