package com.github.kmkt.util.concurrent; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * TaskWorkerRunner の supplier と collector に同一インスタンスを与えることで * 登録された TaskWorker をラウンドロビンで利用する TaskWorker キュー * TaskWorkerRunner での並行実行数はこのキューに登録された TaskWorker の数で制限されます * * @param <T> TaskWorker の引数の型 * @param <R> TaskWorker の返り値の型 */ public class TaskWorkerCyclicQueue<T, R> implements TaskWorkerSupplier<T, R>, TaskWorkerCollector { private static final Logger logger = LoggerFactory.getLogger(TaskWorkerCyclicQueue.class); /** Worker キュー本体 */ private final BlockingQueue<TaskWorker<T, R>> workerQueue = new LinkedBlockingQueue<>(); /** 実行中のため collect 時に削除予定の TaskWorker */ private final Map<TaskWorker<T, R>, SimpleFuture<Boolean>> disabledWorker = new HashMap<>(); /** この Worker キューの管理下にある TaskWorker */ private final Set<TaskWorker<T, R>> containsWorker = new HashSet<>(); /** * @inheritDoc */ @Override public void collect(TaskWorker<?, ?> worker) { try { @SuppressWarnings("unchecked") TaskWorker<T, R> workeragt = (TaskWorker<T, R>) worker; synchronized (containsWorker) { SimpleFuture<Boolean> f = disabledWorker.get(workeragt); if (f != null) { // 削除予定に入っているので回収せずに return containsWorker.remove(workeragt); f.set(new Boolean(true)); return; } } workerQueue.put(workeragt); // 回収 } catch (ClassCastException e) { logger.error("Invalid worker", e); } catch (InterruptedException e) { logger.error("InterruptedException at enqueuing a worker", e); } } /** * @inheritDoc */ @Override public TaskWorker<T, R> get() { try { return workerQueue.take(); } catch (InterruptedException e) { logger.error("InterruptedException at dequeuing a worker", e); return null; } } /** * TaskWorker を Worker キューに登録する * @param worker Worker キューに登録する TaskWorker notnull * @return 登録に成功した場合に true */ public boolean addTaskWorker(TaskWorker<T, R> worker) { Objects.requireNonNull(worker, "worker should not be null"); synchronized (containsWorker) { if (containsWorker.contains(worker)) throw new IllegalArgumentException("worker is already added"); boolean result = workerQueue.add(worker); if (result) { containsWorker.add(worker); } return result; } } /** * TaskWorker を Worker キューに登録する * @param workers null を含まない TaskWorker の Set notnull * @return workers 全ての add に成功した場合に true */ public boolean addTaskWorker(Set<TaskWorker<T, R>> workers) { Objects.requireNonNull(workers, "workers should not be null"); for (TaskWorker<T, R> worker : workers) { if (worker == null) throw new NullPointerException("workers should not have null worker"); } boolean result = true; for (TaskWorker<T, R> worker : workers) { result &= this.addTaskWorker(worker); } return result; // 全ての add に成功した場合に true } /** * TaskWorker を Worker キューから削除する * 対象の TaskWorker が実行中の場合は実行終了時に Worker キューから削除される * 削除の完了は返り値の Future により知ることができる * * @param worker Worker キューから削除する TaskWorker * @return 削除の完了を通知する Future */ public Future<Boolean> removeTaskWorker(TaskWorker<T, R> worker) { synchronized (containsWorker) { if (!containsWorker.contains(worker)) throw new IllegalArgumentException("worker is not contained"); SimpleFuture<Boolean> future = new SimpleFuture<>(); if (workerQueue.remove(worker)) { // workerQueue に含まれており削除に成功 containsWorker.remove(worker); future.set(new Boolean(true)); // done removing } else { // 実行中のため削除予定に登録 disabledWorker.put(worker, future); } return future; } } /** * Worker キューに登録されている TaskWorker 数を取得する * @return Worker キューに登録されている TaskWorker 数 */ public int getNumOfTaskWorker() { synchronized (containsWorker) { return containsWorker.size(); } } }