package com.litesuits.http.concurrent; import android.util.Log; import com.litesuits.http.log.HttpLog; import com.litesuits.http.utils.HttpUtil; import java.util.LinkedList; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; /** * A smart thread pool executor, about {@link SmartExecutor}: * * <ul> * <li>keep {@link #coreSize} tasks concurrent, and put them in {@link #runningList}, * maximum number of running-tasks at the same time is {@link #coreSize}.</li> * <li>when {@link #runningList} is full, put new task in {@link #waitingList} waiting for execution, * maximum of waiting-tasks number is {@link #queueSize}.</li> * <li>when {@link #waitingList} is full, new task is performed by {@link OverloadPolicy}.</li> * <li>when running task is completed, take it out from {@link #runningList}.</li> * <li>schedule next by {@link SchedulePolicy}, take next task out from {@link #waitingList} to execute, * and so on until {@link #waitingList} is empty.</li> * * </ul> * * @author MaTianyu * @date 2015-04-23 */ public class SmartExecutor implements Executor { private static final String TAG = SmartExecutor.class.getSimpleName(); private static final int CPU_CORE = HttpUtil.getCoresNumbers(); private static final int DEFAULT_CACHE_SENCOND = 5; private static ThreadPoolExecutor threadPool; private int coreSize = CPU_CORE; private int queueSize = coreSize * 32; private final Object lock = new Object(); private LinkedList<WrappedRunnable> runningList = new LinkedList<WrappedRunnable>(); private LinkedList<WrappedRunnable> waitingList = new LinkedList<WrappedRunnable>(); private SchedulePolicy schedulePolicy = SchedulePolicy.FirstInFistRun; private OverloadPolicy overloadPolicy = OverloadPolicy.DiscardOldTaskInQueue; public SmartExecutor() { initThreadPool(); } public SmartExecutor(int coreSize, int queueSize) { this.coreSize = coreSize; this.queueSize = queueSize; initThreadPool(); } protected synchronized void initThreadPool() { if (HttpLog.isPrint) { HttpLog.v(TAG, "SmartExecutor core-queue size: " + coreSize + " - " + queueSize + " running-wait task: " + runningList.size() + " - " + waitingList.size()); } if (threadPool == null) { threadPool = createDefaultThreadPool(); } } public static ThreadPoolExecutor createDefaultThreadPool() { return new ThreadPoolExecutor( CPU_CORE, Integer.MAX_VALUE, DEFAULT_CACHE_SENCOND, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() { static final String NAME = "lite-"; AtomicInteger IDS = new AtomicInteger(1); @Override public Thread newThread(Runnable r) { return new Thread(r, NAME + IDS.getAndIncrement()); } }, new ThreadPoolExecutor.DiscardPolicy()); } public static void setThreadPool(ThreadPoolExecutor threadPool) { SmartExecutor.threadPool = threadPool; } public static ThreadPoolExecutor getThreadPool() { return threadPool; } public boolean cancelWaitingTask(Runnable command) { boolean removed = false; synchronized (lock) { int size = waitingList.size(); if (size > 0) { for (int i = size - 1; i >= 0; i--) { if (waitingList.get(i).getRealRunnable() == command) { waitingList.remove(i); removed = true; } } } } return removed; } interface WrappedRunnable extends Runnable { Runnable getRealRunnable(); } protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); } protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); } /** * submit runnable */ public Future<?> submit(Runnable task) { RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; } /** * submit runnable */ public <T> Future<T> submit(Runnable task, T result) { RunnableFuture<T> ftask = newTaskFor(task, result); execute(ftask); return ftask; } /** * submit callable */ public <T> Future<T> submit(Callable<T> task) { RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; } /** * submit RunnableFuture task */ public <T> void submit(RunnableFuture<T> task) { execute(task); } /** * When {@link #execute(Runnable)} is called, {@link SmartExecutor} perform actions: * <ol> * <li>if fewer than {@link #coreSize} tasks are running, post new task in {@link #runningList} and execute it immediately.</li> * <li>if more than {@link #coreSize} tasks are running, and fewer than {@link #queueSize} tasks are waiting, put task in {@link #waitingList}.</li> * <li>if more than {@link #queueSize} tasks are waiting ,schedule new task by {@link OverloadPolicy}</li> * <li>if running task is completed, schedule next task by {@link SchedulePolicy} until {@link #waitingList} is empty.</li> * </ol> */ @Override public void execute(final Runnable command) { if (command == null) { return; } WrappedRunnable scheduler = new WrappedRunnable() { @Override public Runnable getRealRunnable() { return command; } public Runnable realRunnable; @Override public void run() { try { command.run(); } finally { scheduleNext(this); } } }; boolean callerRun = false; synchronized (lock) { //if (HttpLog.isPrint) { // HttpLog.v(TAG, "SmartExecutor core-queue size: " + coreSize + " - " + queueSize // + " running-wait task: " + runningList.size() + " - " + waitingList.size()); //} if (runningList.size() < coreSize) { runningList.add(scheduler); threadPool.execute(scheduler); //HttpLog.v(TAG, "SmartExecutor task execute"); } else if (waitingList.size() < queueSize) { waitingList.addLast(scheduler); //HttpLog.v(TAG, "SmartExecutor task waiting"); } else { //if (HttpLog.isPrint) { // HttpLog.w(TAG, "SmartExecutor overload , policy is: " + overloadPolicy); //} switch (overloadPolicy) { case DiscardNewTaskInQueue: waitingList.pollLast(); waitingList.addLast(scheduler); break; case DiscardOldTaskInQueue: waitingList.pollFirst(); waitingList.addLast(scheduler); break; case CallerRuns: callerRun = true; break; case DiscardCurrentTask: break; case ThrowExecption: throw new RuntimeException("Task rejected from lite smart executor. " + command.toString()); default: break; } } //printThreadPoolInfo(); } if (callerRun) { if (HttpLog.isPrint) { HttpLog.i(TAG, "SmartExecutor task running in caller thread"); } command.run(); } } private void scheduleNext(WrappedRunnable scheduler) { synchronized (lock) { boolean suc = runningList.remove(scheduler); //if (HttpLog.isPrint) { // HttpLog.v(TAG, "Thread " + Thread.currentThread().getName() // + " is completed. remove prior: " + suc + ", try schedule next.."); //} if (!suc) { runningList.clear(); HttpLog.e(TAG, "SmartExecutor scheduler remove failed, so clear all(running list) to avoid unpreditable error : " + scheduler); } if (waitingList.size() > 0) { WrappedRunnable waitingRun; switch (schedulePolicy) { case LastInFirstRun: waitingRun = waitingList.pollLast(); break; case FirstInFistRun: waitingRun = waitingList.pollFirst(); break; default: waitingRun = waitingList.pollLast(); break; } if (waitingRun != null) { runningList.add(waitingRun); threadPool.execute(waitingRun); HttpLog.v(TAG, "Thread " + Thread.currentThread().getName() + " execute next task.."); } else { HttpLog.e(TAG, "SmartExecutor get a NULL task from waiting queue: " + Thread.currentThread().getName()); } } else { if (HttpLog.isPrint) { HttpLog.v(TAG, "SmartExecutor: all tasks is completed. current thread: " + Thread.currentThread().getName()); //printThreadPoolInfo(); } } } } public void printThreadPoolInfo() { if (HttpLog.isPrint) { Log.i(TAG, "___________________________"); Log.i(TAG, "state (shutdown - terminating - terminated): " + threadPool.isShutdown() + " - " + threadPool.isTerminating() + " - " + threadPool.isTerminated()); Log.i(TAG, "pool size (core - max): " + threadPool.getCorePoolSize() + " - " + threadPool.getMaximumPoolSize()); Log.i(TAG, "task (active - complete - total): " + threadPool.getActiveCount() + " - " + threadPool.getCompletedTaskCount() + " - " + threadPool.getTaskCount()); Log.i(TAG, "waitingList size : " + threadPool.getQueue().size() + " , " + threadPool.getQueue()); } } public int getCoreSize() { return coreSize; } public int getRunningSize() { return runningList.size(); } public int getWaitingSize() { return waitingList.size(); } /** * Set maximum number of concurrent tasks at the same time. * Recommended core size is CPU count. * * @param coreSize number of concurrent tasks at the same time * @return this */ public SmartExecutor setCoreSize(int coreSize) { if (coreSize <= 0) { throw new NullPointerException("coreSize can not <= 0 !"); } this.coreSize = coreSize; if (HttpLog.isPrint) { HttpLog.v(TAG, "SmartExecutor core-queue size: " + coreSize + " - " + queueSize + " running-wait task: " + runningList.size() + " - " + waitingList.size()); } return this; } public int getQueueSize() { return queueSize; } /** * Adjust maximum number of waiting queue size by yourself or based on phone performance. * For example: CPU count * 32; * * @param queueSize waiting queue size * @return this */ public SmartExecutor setQueueSize(int queueSize) { if (queueSize < 0) { throw new NullPointerException("queueSize can not < 0 !"); } this.queueSize = queueSize; if (HttpLog.isPrint) { HttpLog.v(TAG, "SmartExecutor core-queue size: " + coreSize + " - " + queueSize + " running-wait task: " + runningList.size() + " - " + waitingList.size()); } return this; } public OverloadPolicy getOverloadPolicy() { return overloadPolicy; } public void setOverloadPolicy(OverloadPolicy overloadPolicy) { if (overloadPolicy == null) { throw new NullPointerException("OverloadPolicy can not be null !"); } this.overloadPolicy = overloadPolicy; } public SchedulePolicy getSchedulePolicy() { return schedulePolicy; } public void setSchedulePolicy(SchedulePolicy schedulePolicy) { if (schedulePolicy == null) { throw new NullPointerException("SchedulePolicy can not be null !"); } this.schedulePolicy = schedulePolicy; } }