日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java Review - 使用Timer时需要注意的事情

發(fā)布時(shí)間:2025/3/21 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java Review - 使用Timer时需要注意的事情 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 概述
  • 問題復(fù)現(xiàn)
  • 源碼分析
    • 源碼分析
  • How to Fix
    • 方法一 : run方法內(nèi)最好使用try-catch結(jié)構(gòu)捕捉可能的異常,不要把異常拋到run方法之外
    • 方法二: ScheduledThreadPoolExecutor (推薦)
  • 小結(jié)


概述

先說結(jié)論 當(dāng)一個(gè)Timer運(yùn)行多個(gè)TimerTask時(shí),只要其中一個(gè)TimerTask在執(zhí)行中向run方法外拋出了異常,則其他任務(wù)也會(huì)自動(dòng)終止

我們看插件的提示


問題復(fù)現(xiàn)

import java.util.Timer; import java.util.TimerTask;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/11/21 20:28* @mark: show me the code , change the world*/ public class TimerTest {// 定時(shí)器static Timer timer = new Timer();public static void main(String[] args) {// 任務(wù)1 , 延遲500ms執(zhí)行 1秒執(zhí)行一次timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Task1 Running");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 模擬發(fā)生異常throw new RuntimeException();}},500,1000);// 任務(wù)2, 延遲1000ms執(zhí)行 1秒執(zhí)行一次timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Task2 Running");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}},1000,1000);} }

如上代碼首先添加了第一個(gè)任務(wù),讓其在500ms后執(zhí)行。然后添加了第二個(gè)任務(wù)在1s后執(zhí)行,我們期望當(dāng)?shù)谝粋€(gè)任務(wù)輸出Task1 Running后,等待1s,第二個(gè)任務(wù)輸出Task1 Running,,然后循環(huán),每隔1秒執(zhí)行一次。

但是執(zhí)行代碼后,輸出結(jié)果為


源碼分析

Timer的原理模型如下

  • TaskQueue是一個(gè)由平衡二叉樹堆實(shí)現(xiàn)的優(yōu)先級(jí)隊(duì)列,每個(gè)Timer對(duì)象內(nèi)部有一個(gè)TaskQueue隊(duì)列。用戶線程調(diào)用Timer的schedule方法就是把TimerTask任務(wù)添加到TaskQueue隊(duì)列。在調(diào)用schedule方法時(shí),long delay參數(shù)用來指明該任務(wù)延遲多少時(shí)間執(zhí)行。

  • ·TimerThread是具體執(zhí)行任務(wù)的線程,它從TaskQueue隊(duì)列里面獲取優(yōu)先級(jí)最高的任務(wù)進(jìn)行執(zhí)行。需要注意的是,只有執(zhí)行完了當(dāng)前的任務(wù)才會(huì)從隊(duì)列里獲取下一個(gè)任務(wù),而不管隊(duì)列里是否有任務(wù)已經(jīng)到了設(shè)置的delay時(shí)間。一個(gè)Timer只有一個(gè)TimerThread線程,所以可知Timer的內(nèi)部實(shí)現(xiàn)是一個(gè)多生產(chǎn)者-單消費(fèi)者模型


源碼分析

從該實(shí)現(xiàn)模型我們知道,要探究上面的問題只需研究TimerThread的實(shí)現(xiàn)就可以了。TimerThread的run方法的主要邏輯代碼如下。

/*** This "helper class" implements the timer's task execution thread, which* waits for tasks on the timer queue, executions them when they fire,* reschedules repeating tasks, and removes cancelled tasks and spent* non-repeating tasks from the queue.*/ class TimerThread extends Thread {/*** This flag is set to false by the reaper to inform us that there* are no more live references to our Timer object. Once this flag* is true and there are no more tasks in our queue, there is no* work left for us to do, so we terminate gracefully. Note that* this field is protected by queue's monitor!*/boolean newTasksMayBeScheduled = true;/*** Our Timer's queue. We store this reference in preference to* a reference to the Timer so the reference graph remains acyclic.* Otherwise, the Timer would never be garbage-collected and this* thread would never go away.*/private TaskQueue queue;TimerThread(TaskQueue queue) {this.queue = queue;}public void run() {try {mainLoop();} finally {// Someone killed this Thread, behave as if Timer cancelledsynchronized(queue) {newTasksMayBeScheduled = false;queue.clear(); // Eliminate obsolete references}}}/*** The main timer loop. (See class comment.)*/private void mainLoop() {while (true) {try {TimerTask task;boolean taskFired;// 從隊(duì)列里面獲取任務(wù)時(shí)要加鎖synchronized(queue) {.........if (taskFired) // Task fired; run it, holding no lockstask.run();// 執(zhí)行任務(wù)} catch(InterruptedException e) {}}} }

當(dāng)任務(wù)在執(zhí)行過程中拋出InterruptedException之外的異常時(shí),唯一的消費(fèi)線程就會(huì)因?yàn)閽伋霎惓6K止,那么隊(duì)列里的其他待執(zhí)行的任務(wù)就會(huì)被清除


How to Fix

方法一 : run方法內(nèi)最好使用try-catch結(jié)構(gòu)捕捉可能的異常,不要把異常拋到run方法之外

所以在TimerTask的run方法內(nèi)最好使用try-catch結(jié)構(gòu)捕捉可能的異常,不要把異常拋到run方法之外。


方法二: ScheduledThreadPoolExecutor (推薦)

其實(shí)要實(shí)現(xiàn)Timer功能,使用ScheduledThreadPoolExecutor的schedule是比較好的選擇。如果ScheduledThreadPoolExecutor中的一個(gè)任務(wù)拋出異常,其他任務(wù)則不受影響。

public class TimerTest {public static void main(String[] args) throws InterruptedException {ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);scheduledThreadPoolExecutor.schedule(()->{System.out.println("Task1 Running");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 模擬發(fā)生異常throw new RuntimeException();},1, TimeUnit.SECONDS);scheduledThreadPoolExecutor.schedule(()->{System.out.println("Task2 Running");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 關(guān)閉線程池scheduledThreadPoolExecutor.shutdown();},1, TimeUnit.SECONDS);}}

之所以ScheduledThreadPoolExecutor的其他任務(wù)不受拋出異常的任務(wù)的影響,是因?yàn)?strong>在ScheduledThreadPoolExecutor中的ScheduledFutureTask任務(wù)中catch掉了異常 。


小結(jié)

ScheduledThreadPoolExecutor是并發(fā)包提供的組件,其提供的功能包含但不限于Timer。Timer是固定的多線程生產(chǎn)單線程消費(fèi),但是ScheduledThreadPoolExecutor是可以配置的,既可以是多線程生產(chǎn)單線程消費(fèi)也可以是多線程生產(chǎn)多線程消費(fèi),所以在日常開發(fā)中使用定時(shí)器功能時(shí)應(yīng)該優(yōu)先使用ScheduledThreadPoolExecutor

總結(jié)

以上是生活随笔為你收集整理的Java Review - 使用Timer时需要注意的事情的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。