/** * 定時任務 * * @author <a href="mailto:biezhi.me@gmail.com" target="_blank">biezhi</a> * @since 1.0 */ public abstract class TaskKit { private static Logger logger = Logger.getLogger(TaskKit.class); private static ScheduledThreadPoolExecutor taskScheduler = new ScheduledThreadPoolExecutor(getBestPoolSize()); private static List<Timer> timerList = new ArrayList<Timer>(); /** * 當即啓動,並以固定的頻率來運行任務。後續任務的啓動時間不受前次任務延時影響。 * @param task 具體待執行的任務 * @param period 每次執行任務的間隔時間(單位秒) */ public static ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long periodSeconds) { return scheduleAtFixedRate(task, 0, periodSeconds, TimeUnit.SECONDS); } /** * 在指定的延時以後開始以固定的頻率來運行任務。後續任務的啓動時間不受前次任務延時影響。 * @param task 具體待執行的任務 * @param initialDelay 首次執行任務的延時時間 * @param periodSeconds 每次執行任務的間隔時間(單位秒) * @param unit 時間單位 */ public static ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) { return taskScheduler.scheduleAtFixedRate(task, initialDelay, period, unit); } /** * 在指定的時間點開始以固定的頻率運行任務。後續任務的啓動時間不受前次任務延時影響。 * @param task 具體待執行的任務 * @param startTime 首次運行的時間點,支持 "yyyy-MM-dd HH:mm:ss" 格式 * @param period 每次執行任務的間隔時間 * @param unit 時間單位 */ public static void scheduleAtFixedRate(Runnable task, String startTime, long period, TimeUnit unit) throws ParseException { Date dt = DateKit.dateFormat(startTime, "yyyy-MM-dd HH:mm:ss"); scheduleAtFixedRate(task, dt, period, unit); } /** * 在指定的時間點開始以固定的頻率運行任務。後續任務的啓動時間不受前次任務延時影響。 * @param task 具體待執行的任務 * @param startTime 首次運行的時間點 * @param period 每次執行任務的間隔時間 * @param unit 時間單位 */ public static void scheduleAtFixedRate(final Runnable task, Date startTime, final long period, final TimeUnit unit) { final Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { taskScheduler.scheduleAtFixedRate(task, 0, period, unit); timer.cancel(); timerList.remove(timer); } }, startTime); timerList.add(timer); } /** * 當即啓動,兩次任務間保持固定的時間間隔 * @param task 具體待執行的任務 * @param period 兩次任務的間隔時間(單位秒) */ public static ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long periodSeconds) { return scheduleWithFixedDelay(task, 0, periodSeconds, TimeUnit.SECONDS); } /** * 在指定的延時以後啓動,兩次任務間保持固定的時間間隔 * @param task 具體待執行的任務 * @param initialDelay 首次執行任務的延時時間 * @param period 兩次任務的間隔時間(單位秒) * @param unit 時間單位 */ public static ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay, long period, TimeUnit unit) { return taskScheduler.scheduleWithFixedDelay(task, initialDelay, period, unit); } /** * 在指定的時間點啓動,兩次任務間保持固定的時間間隔 * @param task 具體待執行的任務 * @param startTime 首次運行的時間點,支持 "yyyy-MM-dd HH:mm:ss" 格式 * @param period 兩次任務的間隔時間 * @param unit 時間單位 */ public static void scheduleWithFixedDelay(Runnable task, String startTime, long period, TimeUnit unit) throws ParseException { Date dt = DateKit.dateFormat(startTime, "yyyy-MM-dd HH:mm:ss"); scheduleWithFixedDelay(task, dt, period, unit); } /** * 在指定的時間點啓動,兩次任務間保持固定的時間間隔 * @param task 具體待執行的任務 * @param startTime 首次運行的時間點 * @param period 兩次任務的間隔時間 * @param unit 時間單位 */ public static void scheduleWithFixedDelay(final Runnable task, Date startTime, final long period, final TimeUnit unit) { final Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { taskScheduler.scheduleWithFixedDelay(task, 0, period, unit); timer.cancel(); timerList.remove(timer); } }, startTime); timerList.add(timer); } /** * 調整線程池大小 * @param threadPoolSize */ public static void resizeThreadPool(int threadPoolSize) { taskScheduler.setCorePoolSize(threadPoolSize); } /** * 返回定時任務線程池,可作更高級的應用 * @return */ public static ScheduledThreadPoolExecutor getTaskScheduler() { return taskScheduler; } /** * 關閉定時任務服務 * <p>系統關閉時可調用此方法終止正在執行的定時任務,一旦關閉後不容許再向線程池中添加任務,不然會報RejectedExecutionException異常</p> */ public static void depose() { int timerNum = timerList.size(); //清除Timer synchronized (timerList) { for (Timer t: timerList) t.cancel(); timerList.clear(); } List<Runnable> awaitingExecution = taskScheduler.shutdownNow(); logger.info("Tasks stopping. Tasks awaiting execution: %d", timerNum + awaitingExecution.size()); } /** * 重啓動定時任務服務 */ public static void reset() { depose(); taskScheduler = new ScheduledThreadPoolExecutor(getBestPoolSize()); } /** * 根據 Java 虛擬機可用處理器數目返回最佳的線程數。<br> * 最佳的線程數 = CPU可用核心數 / (1 - 阻塞係數),其中阻塞係數這裏設爲0.9 */ private static int getBestPoolSize() { try { // JVM可用處理器的個數 final int cores = Runtime.getRuntime().availableProcessors(); // 最佳的線程數 = CPU可用核心數 / (1 - 阻塞係數) // TODO 阻塞係數是否是須要有個setter方法能讓使用者自由設置呢? return (int)(cores / (1 - 0.9)); } catch (Throwable e) { // 異常發生時姑且返回10個任務線程池 return 10; } } }