/* * Copyright (C) 2015-2016 Jacksgong(blog.dreamtobe.cn) * * 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 cn.dreamtobe.threadpool; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Components of exceed wait thread pool. */ public final class ExceedWait { public static class RejectedHandler implements RejectedExecutionHandler { private final static String TAG = "ExceedWaitRejectedHandler"; @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { if (!executor.isShutdown()) { ThreadPoolLog.d(TAG, "put the rejected command to the exceed queue" + " in the rejectedExecution method: %s", r); ((Queue) executor.getQueue()).putExceed(r, executor); } } } public static class Queue extends SynchronousQueue<Runnable> { private final static String TAG = "ExceedWaitQueue"; private final LinkedBlockingQueue<Runnable> mExceedQueue = new LinkedBlockingQueue<>(); public Queue() { super(true); } @Override public boolean offer(Runnable runnable) { ThreadPoolLog.d(TAG, "offer() called with: runnable = [%s]", runnable); boolean result = super.offer(runnable); ThreadPoolLog.d(TAG, "offer() returned: %B", result); return result; } @Override public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException { ThreadPoolLog.d(TAG, "poll() called with: timeout = [%d], unit = [%s]", timeout, unit); // Step 1. Take a glance, to find out whether there is any item in the main queue. Runnable result = super.poll(); // Step 2. If there isn't any item in the main queue, and the exceed queue is available, // poll item from the exceed queue directly. if (mExceedQueue.size() > 0 && result == null) { result = mExceedQueue.poll(); } // Step 3. If there isn't any item in the main queue and the exceed queue, wait // timeout(unit) time for another thread to insert one in the main queue. if (result == null) { result = super.poll(timeout, unit); } ThreadPoolLog.d(TAG, "poll() returned: %s", result); return result; } @Override public Runnable take() throws InterruptedException { ThreadPoolLog.d(TAG, "take() called"); // Step 1. check whether there are tasks available on the main queue. Runnable result = super.poll(); if (result == null && mExceedQueue.size() > 0) { result = mExceedQueue.poll(); } // Step 2. if there isn't any task whether on the core main exceed queue, take to wait. if (result == null) { result = super.take(); } ThreadPoolLog.d(TAG, "take() returned: %s", result); return result; } @Override public boolean remove(Object o) { ThreadPoolLog.d(TAG, "remove() called with: o = [%s]", o); return mExceedQueue.remove(o); } @Override public boolean removeAll(Collection<?> c) { ThreadPoolLog.d(TAG, "removeAll() called with: c = [%s]", c); return mExceedQueue.removeAll(c); } public int exceedSize() { return mExceedQueue.size(); } public void putExceed(Runnable e, ThreadPoolExecutor executor) { mExceedQueue.offer(e); final int activeCount = executor.getActiveCount(); if (activeCount <= 0) { // In this case( the main queue is waiting for inserting or the active count is less // than 0), we need to wake up the pool manually with the command from the head of // exceed-queue. final Runnable next = mExceedQueue.poll(); if (next != null) { ThreadPoolLog.d(TAG, "putExceed and activeCount(%d), need to " + "wake up the pool with next(%s)", activeCount, next); executor.execute(next); } } } public List<Runnable> drainExceedQueue() { BlockingQueue<Runnable> q = mExceedQueue; ArrayList<Runnable> taskList = new ArrayList<>(); q.drainTo(taskList); if (!q.isEmpty()) { for (Runnable r : q.toArray(new Runnable[0])) { if (q.remove(r)) taskList.add(r); } } return taskList; } } }