一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - Java教程 - Java并發Timer源碼分析

Java并發Timer源碼分析

2021-05-14 11:18狂小白 Java教程

這篇文章講述了java并發編程的相關知識點,并通過Timer源碼分析更深入的講解了java并發編程。

timer在jdk里面,是很早的一個api了。具有延時的,并具有周期性的任務,在newscheduledthreadpool出來之前我們一般會用timer和timertask來做,但是timer存在一些缺陷,為什么這么說呢?

timer只創建唯一的線程來執行所有timer任務。如果一個timer任務的執行很耗時,會導致其他timertask的時效準確性出問題。例如一個timertask每10秒執行一次,而另外一個timertask每40ms執行一次,重復出現的任務會在后來的任務完成后快速連續的被調用4次,要么完全“丟失”4次調用。timer的另外一個問題在于,如果timertask拋出未檢查的異常會終止timer線程。這種情況下,timer也不會重新回復線程的執行了;它錯誤的認為整個timer都被取消了。此時已經被安排但尚未執行的timertask永遠不會再執行了,新的任務也不能被調度了。

這里做了一個小的 demo 來復現問題,代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.hjc;
 
import java.util.timer;
import java.util.timertask;
 
/**
 * created by cong on 2018/7/12.
 */
public class timertest {
 //創建定時器對象
 static timer timer = new timer();
 
 public static void main(string[] args) {
 //添加任務1,延遲500ms執行
 timer.schedule(new timertask() {
 
  @override
  public void run() {
  system.out.println("---one task---");
  try {
   thread.sleep(1000);
  } catch (interruptedexception e) {
   e.printstacktrace();
  }
  throw new runtimeexception("error ");
  }
 }, 500);
 //添加任務2,延遲1000ms執行
 timer.schedule(new timertask() {
 
  @override
  public void run() {
  for (;;) {
   system.out.println("---two task---");
   try {
   thread.sleep(1000);
   } catch (interruptedexception e) {
   // todo auto-generated catch block
   e.printstacktrace();
   }
  }
  }
 }, 1000);
 
 }
}

如上代碼先添加了一個任務在 500ms 后執行,然后添加了第二個任務在 1s 后執行,我們期望的是當第一個任務輸出 ---one task--- 后等待 1s 后第二個任務會輸出 ---two task---,

但是執行完畢代碼后輸出結果如下所示:

Java并發Timer源碼分析

例子2,

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class shedule {
 private static long start;
 
 public static void main(string[] args) {
  timertask task = new timertask() {
   public void run() {
    system.out.println(system.currenttimemillis()-start);
    try{
     thread.sleep(3000);
    }catch (interruptedexception e){
     e.printstacktrace();
    }
   }
  };
 
  timertask task1 = new timertask() {
   @override
   public void run() {
    system.out.println(system.currenttimemillis()-start);
   }
  };
 
  timer timer = new timer();
  start = system.currenttimemillis();
  //啟動一個調度任務,1s鐘后執行
  timer.schedule(task,1000);
  //啟動一個調度任務,3s鐘后執行
  timer.schedule(task1,3000);
 
 
 }
 
}

上面程序我們預想是第一個任務執行后,第二個任務3s后執行的,即輸出一個1000,一個3000.

實際運行結果如下:

Java并發Timer源碼分析

實際運行結果并不如我們所愿。世界結果,是過了4s后才輸出第二個任務,即4001約等于4秒。那部分時間時間到哪里去了呢?那個時間是被我們第一個任務的sleep所占用了。

現在我們在第一個任務中去掉thread.sleep();這一行代碼,運行是否正確了呢?運行結果如下:

Java并發Timer源碼分析

可以看到確實是第一個任務過了1s后執行,第二個任務在第一個任務執行完后過3s執行了。

這就說明了timer只創建唯一的線程來執行所有timer任務。如果一個timer任務的執行很耗時,會導致其他timertask的時效準確性出問題。

timer 實現原理分析

下面簡單介紹下 timer 的原理,如下圖是 timer 的原理模型介紹:

Java并發Timer源碼分析

1.其中 taskqueue 是一個平衡二叉樹堆實現的優先級隊列,每個 timer 對象內部有唯一一個 taskqueue 隊列。用戶線程調用 timer 的 schedule 方法就是把 timertask 任務添加到 taskqueue 隊列,在調用 schedule 的方法時候 long delay 參數用來說明該任務延遲多少時間執行。

2.timerthread 是具體執行任務的線程,它從 taskqueue 隊列里面獲取優先級最小的任務進行執行,需要注意的是只有執行完了當前的任務才會從隊列里面獲取下一個任務而不管隊列里面是否有已經到了設置的 delay 時間,一個 timer 只有一個 timerthread 線程,所以可知 timer 的內部實現是一個多生產者單消費者模型。

從實現模型可以知道要探究上面的問題只需看 timerthread 的實現就可以了,timerthread 的 run 方法主要邏輯源碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void run() {
 try {
  mainloop();
 } finally {
  // 有人殺死了這個線程,表現得好像timer已取消
  synchronized(queue) {
   newtasksmaybescheduled = false;
   queue.clear(); // 消除過時的引用
  }
 }
}
 private void mainloop() {
  while (true) {
   try {
    timertask task;
    boolean taskfired;
    //從隊列里面獲取任務時候要加鎖
    synchronized(queue) {
     ......
    }
    if (taskfired)
     task.run();//執行任務
   } catch(interruptedexception e) {
   }
  }
 }

可知當任務執行過程中拋出了除 interruptedexception 之外的異常后,唯一的消費線程就會因為拋出異常而終止,那么隊列里面的其他待執行的任務就會被清除。所以 timertask 的 run 方法內最好使用 try-catch 結構 catch 主可能的異常,不要把異常拋出到 run 方法外。

其實要實現類似 timer 的功能使用 scheduledthreadpoolexecutor 的 schedule 是比較好的選擇。scheduledthreadpoolexecutor 中的一個任務拋出了異常,其他任務不受影響的。

scheduledthreadpoolexecutor 例子如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
 * created by cong on 2018/7/12.
 */
public class scheduledthreadpoolexecutortest {
 static scheduledthreadpoolexecutor scheduledthreadpoolexecutor = new scheduledthreadpoolexecutor(1);
 
 public static void main(string[] args) {
 
  scheduledthreadpoolexecutor.schedule(new runnable() {
 
   public void run() {
    system.out.println("---one task---");
    try {
     thread.sleep(1000);
    } catch (interruptedexception e) {
     e.printstacktrace();
    }
    throw new runtimeexception("error ");
   }
 
  }, 500, timeunit.microseconds);
 
  scheduledthreadpoolexecutor.schedule(new runnable() {
 
   public void run() {
    for (int i =0;i<5;++i) {
     system.out.println("---two task---");
     try {
      thread.sleep(1000);
     } catch (interruptedexception e) {
      e.printstacktrace();
     }
    }
 
   }
 
  }, 1000, timeunit.microseconds);
 
  scheduledthreadpoolexecutor.shutdown();
 }
}

運行結果如下:

Java并發Timer源碼分析

之所以 scheduledthreadpoolexecutor 的其他任務不受拋出異常的任務的影響是因為 scheduledthreadpoolexecutor 中的 scheduledfuturetask 任務中 catch 掉了異常,但是在線程池任務的 run 方法內使用 catch 捕獲異常并打印日志是最佳實踐。

原文鏈接:https://www.cnblogs.com/huangjuncong/p/9296897.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲精品有码在线观看 | 91香蕉国产在线观看人员 | 日韩一级精品视频在线观看 | 国产成人8x视频一区二区 | 国产精品久久国产精品99 | 4hu永久地域网名入口 | 久久性综合亚洲精品电影网 | 男人的j放进女人的p全黄 | 四虎国产精品视频免费看 | 欧美日韩国产一区二区三区不卡 | 火影小南被爆羞羞网站进入 | 亚洲精品色婷婷在线影院麻豆 | 国产香蕉97碰碰久久人人 | 小鸟酱喷水| 色哟哟哟在线精品观看视频 | 午夜办公室在线观看高清电影 | 精品国产区一区二区三区在线观看 | 午夜AV国产欧美亚洲高清在线 | 和两个男人玩3p好爽视频 | 亚洲国产精品自在自线观看 | 成年人在线视频观看 | 色婷婷在线播放 | 亚洲AV综合99一二三四区 | 久久婷婷五月免费综合色啪 | 湖南美女被黑人4p到惨叫 | 成年视频在线播放 | 国产亚洲精品美女2020久久 | 日韩理论片在线看免费观看 | 好大好深视频 | 91免费永久在线地址 | 春光乍泄在线 | 无敌在线视频观看免费 | 欧美日韩国产最新一区二区 | 日比免费视频 | 四缺一写的小说 | 欧美乱码视频 | 天天操丝袜 | 免费真实播放国产乱子伦 | 国产日韩精品一区二区在线观看 | 亚洲AV无码国产精品色在线看 | kkkk4444在线看片免费 |