一、 使用 while(true) 和 sleep 實現
new Thread(){ @Override public void run() { while (true) { System.out.println("Hello!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start();
二、使用 Timer 和 TimerTask 實現
/** * 一個 Timer 對應一個線程,用於執行任務 * @param name 線程名字 * @param isDaemon 是否為當前線程的守護線程 */ Timer timer = new Timer("haha", false); // 一個 TimerTask 對應一個任務 TimerTask task1 = new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "Hello! A"); } }; // 立即執行,只執行一次 timer.schedule(task1, 0); timer.schedule(task1, 0, 1000); TimerTask task2 = new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "Hello! B"); } }; // 立即執行,然后每秒執行一次 timer.scheduleAtFixedRate(task2, 0, 1000); Thread.sleep(5000); // 取消任務 task2.cancel(); Thread.sleep(5000); // 將所有已經取消的任務移除(釋放資源) timer.purge(); // 停止 timer 中的所有任務,並終止計時器,無法再提交任務 timer.cancel();
schedule 與 scheduleAtFixedRate 區別
schedule 注重間隔時間,不管任務執行需要多長時間,下一次執行都是在執行完成后的指定間隔時間再執行。
scheduleAtFixedRate 注重執行次數,例如當任務太多或其他原因導致某段時間內執行次數不夠(總時間/間隔時間),則會嘗試縮短間隔時間,保證總體執行次數。
三、使用 ScheduledThreadPoolExecutor 實現
阿里巴巴 Java 手冊中的片段
多線程並行處理定時任務時,Timer 運行多個 TimeTask 時,只要其中之一沒有捕獲拋出的異常,其它任務便會自動終止運行,使用 ScheduledExecutorService 則沒有這個問題。 線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。 說明:Executors 返回的線程池對象的弊端如下: 1)FixedThreadPool 和 SingleThreadPool: 允許的請求隊列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。 2)CachedThreadPool 和 ScheduledThreadPool: 允許的創建線程數量為 Integer.MAX_VALUE,可能會創建大量的線程,從而導致 OOM。
說明了創建線程池的規范,以及使用 Timer 的問題
// Guava 庫的工具類,線程工廠,這里主要用來設置線程名字 ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build(); ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(10, namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); // 一秒后執行 stpe.schedule(() -> { System.out.println(Thread.currentThread().getName() + "\tA"); }, 1, TimeUnit.SECONDS); // 立即執行,然后每秒執行一次,注重次數 stpe.scheduleAtFixedRate(() -> { System.out.println(Thread.currentThread().getName() + "\tB"); }, 0, 1, TimeUnit.SECONDS); // 立即執行,然后每兩秒執行一次,注重間隔時間 stpe.scheduleWithFixedDelay(() -> { System.out.println(Thread.currentThread().getName() + "\tC"); }, 0, 2, TimeUnit.SECONDS); Thread.sleep(10000); stpe.purge(); // 關閉 stpe.shutdown();