/* * Copyright 2016 NAVER Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.navercorp.pinpoint.web.util; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @author Woonduk Kang(emeroad) */ public class SimpleOrderedThreadPool implements Executor { private final int threadCount; private final int workerQueueSize; private final ThreadFactory threadFactory; private final ExecutorService[] childExecutors; public SimpleOrderedThreadPool(int threadCount, int workerQueueSize, ThreadFactory threadFactory) { if (threadFactory == null) { throw new NullPointerException("threadFactory must not be null"); } if (threadCount < 0) { throw new IllegalArgumentException("threadCount workerQueueSize"); } if (workerQueueSize < 0) { throw new IllegalArgumentException("workerQueueSize workerQueueSize"); } this.threadCount = threadCount; this.workerQueueSize = workerQueueSize; this.threadFactory = threadFactory; this.childExecutors = createChildExecutor(threadCount); } private ExecutorService[] createChildExecutor(int threadCount) { final ExecutorService[] childExecutor = new ExecutorService[threadCount]; for (int i = 0; i < threadCount; i++) { final ExecutorService child = createSingleThreadExecutor(this.workerQueueSize, threadFactory); childExecutor[i] = child; } return childExecutor; } private ExecutorService createSingleThreadExecutor(int workerQueueSize, ThreadFactory threadFactory) { final LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(workerQueueSize); return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, workQueue, threadFactory); } @Override public void execute(Runnable command) { final ExecutorService childExecutor = getChildExecutor(command); dispatchChild(childExecutor, command); } private void dispatchChild(ExecutorService childExecutor, Runnable command) { childExecutor.execute(command); } private ExecutorService getChildExecutor(Runnable command) { if (!(command instanceof HashSelector)) { throw new IllegalArgumentException("invalid HashSelector command"); } final HashSelector selector = (HashSelector) command; final int mod = mod(selector); return this.childExecutors[mod]; } private int mod(HashSelector selector) { final int id = selector.select(); final int mod = id % threadCount; return Math.abs(mod); } public void shutdown() { for (ExecutorService executorService : childExecutors) { executorService.shutdown(); } } public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { boolean totalShutdown = true; for (ExecutorService executorService : childExecutors) { final boolean child = executorService.awaitTermination(timeout, unit); if (!child) { totalShutdown = false; } // TODO timeout calculation // remainTimeout = remainTimeout - childShutdownTime ~~~~~ } return totalShutdown; } public interface HashSelector { int select(); } }