<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 167,  comments - 30,  trackbacks - 0

    jdk并發部分,如果英文理解有點小難,可以參考http://www.yiibai.com/java6/java/util/concurrent/package-summary.html
    本篇轉自:http://victorzhzh.iteye.com/blog/1011635

    很多時候我們希望任務可以定時的周期性的執行,在最初的JAVA工具類庫中,通過Timer可以實現定時的周期性的需求,但是有一定的缺陷,例如:Timer是基于絕對時間的而非支持相對時間,因此Timer對系統時鐘比較敏感。雖然有一定的問題,但是我們還是從這個最簡單的實現開始研究。

     

    首先,我們準備一些討論問題的類:TimerTask1和TimerLongTask,如下

    Java代碼  收藏代碼
    1. public class TimerTask1 extends TimerTask {  
    2.   
    3.     @Override  
    4.     public void run() {  
    5.         String base = "abcdefghijklmnopqrstuvwxyz0123456789";  
    6.         Random random = new Random();  
    7.         StringBuffer sb = new StringBuffer();  
    8.         for (int i = 0; i < 10; i++) {  
    9.             int number = random.nextInt(base.length());  
    10.             sb.append(base.charAt(number));  
    11.         }  
    12.         System.out.println(new Date()+","+sb.toString());  
    13.     }  
    14.   
    15. }  

     這個類負責生成一個含有10個字符的字符串,這里我們將輸出時間打印出來,近似認為是任務執行的時間。

    Java代碼  收藏代碼
    1. public class TimerLongTask extends TimerTask {  
    2.   
    3.     @Override  
    4.     public void run() {  
    5.         System.out.println("TimerLongTask: 開始沉睡");  
    6.         try {  
    7.             TimeUnit.SECONDS.sleep(10);  
    8.         } catch (InterruptedException e) {  
    9.             e.printStackTrace();  
    10.         }  
    11.         System.out.println("TimerLongTask: 已經醒來");  
    12.     }  
    13.   
    14. }  

     這個類啟動了一個長任務,即讓任務沉睡10秒。

     

    下面我們來看一個定時任務執行的例子:

    Java代碼  收藏代碼
    1. public static void main(String[] args) throws InterruptedException {  
    2.     Timer timer = new Timer();  
    3.     timer.schedule(new TimerTask1(), 1);  
    4.     timer.schedule(new TimerLongTask(), 1);  
    5.     timer.schedule(new TimerTask1(), 2);  
    6.     TimeUnit.SECONDS.sleep(20);  
    7.     timer.cancel();  
    8. }  

     在這個例子中,我們先提交了一個TimerTask1任務,且讓它延遲1毫秒執行,緊接著我們又提交了一個TimerLongTask長任務,且讓它也延遲1毫秒執行,最后我們在提交一個TimerTask1任務,延遲2毫秒執行。然后讓主線程沉睡20秒后關閉timer。我們看一下執行結果:

    Java代碼  收藏代碼
    1. Thu Apr 21 11:04:31 CST 2011,utg3hn7u4r  
    2. TimerLongTask: 開始沉睡  
    3. TimerLongTask: 已經醒來  
    4. Thu Apr 21 11:04:41 CST 2011,4aac22sud1  

     這里我們看到第一次輸出10個字符的時間和第二次輸出10個字符的時間上相差了10秒,這10秒恰恰是長任務沉睡的時間,通過這個輸出我們可以分析出:Timer用來執行任務的線程其實只有一個,且逐一被執行。接下來我們查看一下源碼驗證一下,如下:

    Java代碼  收藏代碼
    1. private TaskQueue queue = new TaskQueue();  
    2. private TimerThread thread = new TimerThread(queue);  

     這兩行代碼來自Timer源碼,我們可以看到在第一次創建了Timer時就已經創建了一個thread和一個queue,因此只有一個線程來執行我們的任務。

    那么Timer是如何來執行任務的?

    首先我們調用timer.schedule方法,將任務提交到timer中,Timer中有很多重載的schedule方法,但它們都會調用同一個方法即sched方法。這個方法會將我們提交的任務添加到TaskQueue的隊列中(即queue),在每次添加時都會根據nextExecutionTime大小來調整隊列中任務的順序,讓nextExecutionTime最小的排在隊列的最前端,nextExecutionTime最大的排在隊列的最后端。在創建Timer時,我們同時也創建了一個TimerThread即thread,并且啟動了這個線程,

    Java代碼  收藏代碼
    1. public Timer(String name) {  
    2.         thread.setName(name);  
    3.         thread.start();  
    4. }  

     TimerThread中的mainLoop方法是核心,它會完成所有的任務執行,在一開始我們的隊列為空,這時mainLoop方法將會使線程進入等待狀態,當我們使用schedule提交任務時會notify這個TimerThread線程,若任務的執行未到則在wait相對的時間差。

    我們調整一下上面的代碼,

    Java代碼  收藏代碼
    1. Timer timer = new Timer();  
    2. timer.schedule(new TimerTask1(), 1);  
    3. timer.schedule(new TimerTask1(), 5000);  
    4. timer.schedule(new TimerLongTask(), 3000);  
    5. TimeUnit.SECONDS.sleep(20);  
    6. timer.cancel();  

     這樣先提交兩個輸出字符的任務最后提交長任務,在這里,我們讓第二個輸出字符的任務延遲5秒執行,長任務延遲3秒執行,這樣得到的結果如下:

    Java代碼  收藏代碼
    1. Thu Apr 21 13:07:44 CST 2011,2sstwluvgc  
    2. TimerLongTask: 開始沉睡  
    3. TimerLongTask: 已經醒來  
    4. Thu Apr 21 13:07:57 CST 2011,sh4fnkqqc8  

     雖然我們改變了提交順序,但是還是按照延遲時間遞增排序執行的,兩個輸出字符串的時間之間相差13秒,這也是長任務等待執行時間+長任務睡眠時間之和。

     

    重復執行scheduleAtFixedRate方法提交任務,主要是調用rescheduleMin方法對已經調用的任務進行重新設置調度延遲,并調用fixDown方法對隊列里的任務根據延遲時間重新排序。

    Java代碼  收藏代碼
    1. Timer timer = new Timer();  
    2. timer.scheduleAtFixedRate(new TimerTask1(), 30005000);  

     3000,代表第一次執行時等待的時間,5000代表每次執行任務之間的時間間隔,運行結果:

    Java代碼  收藏代碼
    1. Thu Apr 21 13:14:55 CST 2011,izf536esrg  
    2. Thu Apr 21 13:15:00 CST 2011,2khzm7e09v  
    3. Thu Apr 21 13:15:05 CST 2011,jc3dvt2m8q  

     基本是每5秒運行一次。

     

    由于Timer只使用一個線程運行所有的任務,那么當一個任務拋出運行時異常后會有什么樣的情形呢?其他的任務是否可以繼續?我們已經有了前面的知識可以先猜想一個結果:因為Timer只使用一個線程運行所有的任務,所以當一個線程拋出運行時異常時,這個線程就基本掛了,不會在執行后續的任何代碼,因此我們可以斷言,當一個任務拋出運行時異常時,后續任務都不可以執行。為了證明這個猜想,我們需要一個可以拋出異常的任務,如下:

    Java代碼  收藏代碼
    1. public class TimerExceptionTask extends TimerTask {  
    2.   
    3.     @Override  
    4.     public void run() {  
    5.         System.out.println("TimerExceptionTask: "+new Date());  
    6.         throw new RuntimeException();  
    7.     }  
    8.   
    9. }  

     這個任務拋出一個運行時異常。接著我們需要定義一下我們任務執行的順序:先執行一個正常的任務,然后在執行一個拋出異常的任務,最后在執行一個正常的任務,如下:

    Java代碼  收藏代碼
    1. Timer timer = new Timer();  
    2. timer.schedule(new TimerTask1(), 1000);  
    3. timer.schedule(new TimerExceptionTask(), 3000);  
    4. timer.schedule(new TimerTask1(), 5000);  
    5. TimeUnit.SECONDS.sleep(6);  
    6. timer.cancel();  

     延遲1秒執行正常輸出字符串的任務,延遲3秒執行拋出異常的任務,延遲5秒執行正常輸出字符串的任務,看一下結果:

    Java代碼  收藏代碼
    1. Thu Apr 21 13:40:23 CST 2011,lk7fjneyyu  
    2. TimerExceptionTask: Thu Apr 21 13:40:25 CST 2011  
    3. Exception in thread "Timer-0" java.lang.RuntimeException  
    4.     at org.victorzhzh.concurrency.TimerExceptionTask.run(TimerExceptionTask.java:11)  
    5.     at java.util.TimerThread.mainLoop(Timer.java:512)  
    6.     at java.util.TimerThread.run(Timer.java:462)  

     并沒有輸出兩個字符串,只執行了第一次的輸出字符串任務,說明當拋出運行時異常時,其后續任務不可能被執行。

    鑒于Timer的缺陷,所以對它的使用還是要謹慎的,還好并發包中為我們提供了相應的替代品:


    我們將圍繞這些不足之處看看ScheduledThreadPoolExecutor是如何優化的。

    為了研究方便我們需要兩個類:

    Java代碼  收藏代碼
    1. public class Task1 implements Callable<String> {  
    2.   
    3.     @Override  
    4.     public String call() throws Exception {  
    5.         String base = "abcdefghijklmnopqrstuvwxyz0123456789";  
    6.         Random random = new Random();  
    7.         StringBuffer sb = new StringBuffer();  
    8.         for (int i = 0; i < 10; i++) {  
    9.             int number = random.nextInt(base.length());  
    10.             sb.append(base.charAt(number));  
    11.         }  
    12.         System.out.println("Task1 running: " + new Date());  
    13.         return sb.toString();  
    14.     }  
    15.   
    16. }  

     生成含有10個字符的字符串,使用Callable接口目的是我們不再任務中直接輸出結果,而主動取獲取任務的結果

    Java代碼  收藏代碼
    1. public class LongTask implements Callable<String> {  
    2.   
    3.     @Override  
    4.     public String call() throws Exception {  
    5.         System.out.println("LongTask running: "+new Date());  
    6.         TimeUnit.SECONDS.sleep(10);  
    7.         return "success";  
    8.     }  
    9.   
    10. }  

    長任務類,這里我們讓任務沉睡10秒然后返回一個“success”

    下面我們來分析一下ScheduledThreadPoolExecutor:

    1、Timer中單線程問題是否在ScheduledThreadPoolExecutor中存在?

    我們先來看一下面的程序:

    Java代碼  收藏代碼
    1. public class ScheduledThreadPoolExec {  
    2.     public static void main(String[] args) throws InterruptedException,  
    3.             ExecutionException {  
    4.         <strong><span style="color: #ff0000;">ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(  
    5.                 2);</span>  
    6. </strong>  
    7.   
    8.   
    9.         ScheduledFuture future1 = executor.schedule(new Task1(), 5,  
    10.                 TimeUnit.SECONDS);  
    11.         ScheduledFuture future2 = executor.schedule(new LongTask(), 3,  
    12.                 TimeUnit.SECONDS);  
    13.   
    14.         BlockingQueue<ScheduledFuture> blockingQueue = new ArrayBlockingQueue<ScheduledFuture>(  
    15.                 2true);  
    16.         blockingQueue.add(future2);  
    17.         blockingQueue.add(future1);  
    18.   
    19.         System.out.println(new Date());  
    20.         while (!blockingQueue.isEmpty()) {  
    21.             ScheduledFuture future = blockingQueue.poll();  
    22.             if (!future.isDone())  
    23.                 blockingQueue.add(future);  
    24.             else  
    25.                 System.out.println(future.get());  
    26.         }  
    27.         System.out.println(new Date());  
    28.         executor.shutdown();  
    29.     }  
    30.   
    31. }  

     首先,我們定義了一個ScheduledThreadPoolExecutor它的池長度是2。接著提交了兩個任務:第一個任務將延遲5秒執行,第二個任務將延遲3秒執行。我們建立了一個BlockingQueue,用它來存儲了ScheduledFuture,使用ScheduledFuture可以獲得任務的執行結果。在一個while循環中,我們每次將一個ScheduledFuture從隊列中彈出,驗證它是否被執行,如果沒有被執行則再次將它加入隊列中,如果被執行了,這使用ScheduledFuture的get方法獲取任務執行的結果。看一下執行結果:

    Java代碼  收藏代碼
    1. Thu Apr 21 19:23:02 CST 2011  
    2. LongTask running: Thu Apr 21 19:23:05 CST 2011  
    3. Task1 running: Thu Apr 21 19:23:07 CST 2011  
    4. h1o2wd942e  
    5. success  
    6. Thu Apr 21 19:23:15 CST 2011  

     我們看到長任務先運行,因為長任務只等待了3秒,然后是輸出字符串的任務運行,兩個任務開始時間相差2秒,而先輸出了字符串而后才是長任務運行的結果success,最后我們查看一下整體的開始和結束時間相差了13秒。

    這說明在ScheduledThreadPoolExecutor中它不是以一個線程運行任務的,而是以多個線程,如果用一個線程運行任務,那么長任務運行完之前是不會運行輸出字符串任務的。其實這個“多個任務“是我們自己指定的注意一下標紅的代碼,如果我們把這行代碼改為:

    Java代碼  收藏代碼
    1. ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);  

     那么運行結果就會發生變化,

    Java代碼  收藏代碼
    1. Thu Apr 21 19:36:56 CST 2011  
    2. LongTask running: Thu Apr 21 19:36:59 CST 2011  
    3. success  
    4. Task1 running: Thu Apr 21 19:37:09 CST 2011  
    5. y981iqd0or  
    6. Thu Apr 21 19:37:09 CST 2011  

     這時其實和使用Timer運行結果是一樣的。任務是在一個線程里順序執行的。

     

    2、Timer中一但有運行時異常報出后續任務是否還會正常運行?

    為了研究這個問題,我們還是需要一個能夠拋出異常的任務,如下:

    Java代碼  收藏代碼
    1. public class TimerExceptionTask extends TimerTask {  
    2.   
    3.     @Override  
    4.     public void run() {  
    5.         System.out.println("TimerExceptionTask: "+new Date());  
    6.         throw new RuntimeException();  
    7.     }  
    8.   
    9. }  

     我們對上面運行任務的代碼做一點點小小的修改,先運行兩個拋出異常的任務,如下:

    Java代碼  收藏代碼
    1. public class ScheduledThreadPoolExec {  
    2.     public static void main(String[] args) throws InterruptedException,  
    3.             ExecutionException {  
    4.         ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(  
    5.                 2);  
    6.         ScheduledFuture future1 = executor.schedule(new Task1(), 5,  
    7.                 TimeUnit.SECONDS);  
    8.         ScheduledFuture future2 = executor.schedule(new LongTask(), 3,  
    9.                 TimeUnit.SECONDS);  
    10.         <strong><span style="color: #ff0000;">executor.schedule(new TimerExceptionTask(), 1, TimeUnit.SECONDS);  
    11.         executor.schedule(new TimerExceptionTask(), 2, TimeUnit.SECONDS);</span>  
    12. </strong>  
    13.   
    14.   
    15.   
    16.         BlockingQueue<ScheduledFuture> blockingQueue = new ArrayBlockingQueue<ScheduledFuture>(  
    17.                 2true);  
    18.         blockingQueue.add(future2);  
    19.         blockingQueue.add(future1);  
    20.   
    21.         System.out.println(new Date());  
    22.         while (!blockingQueue.isEmpty()) {  
    23.             ScheduledFuture future = blockingQueue.poll();  
    24.             if (!future.isDone())  
    25.                 blockingQueue.add(future);  
    26.             else  
    27.                 System.out.println(future.get());  
    28.         }  
    29.         System.out.println(new Date());  
    30.         executor.shutdown();  
    31.     }  
    32.   
    33. }  

     注意,標紅的代碼,如果這兩個代碼拋出錯誤后會影響后續任務,那么就應該在此終止,但是看一下結果,

    Java代碼  收藏代碼
    1. Thu Apr 21 19:40:15 CST 2011  
    2. TimerExceptionTask: Thu Apr 21 19:40:16 CST 2011  
    3. TimerExceptionTask: Thu Apr 21 19:40:17 CST 2011  
    4. LongTask running: Thu Apr 21 19:40:18 CST 2011  
    5. Task1 running: Thu Apr 21 19:40:20 CST 2011  
    6. v5gcf01iiz  
    7. success  
    8. Thu Apr 21 19:40:28 CST 2011  

     后續任務仍然執行,可能會有朋友說:“你上面的池設置的是2,所以很有可能是那兩個拋出異常的任務都在同一個線程中執行,而另一個線程執行了后續的任務”。那我們就把ScheduledThreadPoolExecutor設置成1看看,結果如下:

    Java代碼  收藏代碼
    1. Thu Apr 21 19:43:00 CST 2011  
    2. TimerExceptionTask: Thu Apr 21 19:43:01 CST 2011  
    3. TimerExceptionTask: Thu Apr 21 19:43:02 CST 2011  
    4. LongTask running: Thu Apr 21 19:43:03 CST 2011  
    5. success  
    6. Task1 running: Thu Apr 21 19:43:13 CST 2011  
    7. 33kgv8onnd  
    8. Thu Apr 21 19:43:13 CST 2011  

    后續任務也執行了,所以說ScheduledThreadPoolExecutor不會像Timer那樣有線程泄漏現象。

    對于周期性執行和Timer很類似這里就不再舉例了。

     

    posted on 2013-03-18 18:58 David1228 閱讀(1768) 評論(0)  編輯  收藏 所屬分類: JAVA線程-模式-設計

    <2013年3月>
    242526272812
    3456789
    10111213141516
    17181920212223
    24252627282930
    31123456

    常用鏈接

    留言簿(4)

    隨筆分類

    隨筆檔案

    文章檔案

    新聞分類

    新聞檔案

    相冊

    收藏夾

    Java

    Linux知識相關

    Spring相關

    云計算/Linux/虛擬化技術/

    友情博客

    多線程并發編程

    開源技術

    持久層技術相關

    搜索

    •  

    積分與排名

    • 積分 - 358624
    • 排名 - 154

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲精品免费在线观看| 亚洲天然素人无码专区| 亚洲AV永久无码区成人网站 | 亚洲av最新在线观看网址| 亚洲国产综合精品中文第一| 亚洲成a人片在线观看精品| 亚洲1区1区3区4区产品乱码芒果 | 在线观看成人免费视频| 午夜神器成在线人成在线人免费| 成年美女黄网站色大免费视频| 67194熟妇在线永久免费观看| 免费看污成人午夜网站| 性一交一乱一视频免费看| 黄网址在线永久免费观看| 免费高清在线爱做视频| 免费人妻av无码专区| 国产a v无码专区亚洲av| 亚洲乱码中文字幕综合| 亚洲国产成人久久综合碰碰动漫3d | 精品视频在线免费观看| 久久w5ww成w人免费| 无码国产精品一区二区免费虚拟VR | 亚洲AV日韩AV一区二区三曲| 老司机精品视频免费| 精品国产免费人成网站| 无码人妻一区二区三区免费n鬼沢 无码人妻一区二区三区免费看 | 久久亚洲精品中文字幕三区| 亚洲高清在线视频| 亚洲国产成人超福利久久精品| 中文字幕精品三区无码亚洲| 国产精品亚洲一区二区三区久久| 猫咪www免费人成网站| 丰满人妻一区二区三区免费视频| 久久这里只精品热免费99| 国产成人精品免费视频网页大全| 成人免费一区二区无码视频| 免费一区二区视频| 亚洲AV无码国产精品麻豆天美 | 亚洲精品国产综合久久一线| 亚洲AV无码欧洲AV无码网站| 亚洲人成网站日本片|