Java多線程三

1. 基本概念

  java.util.Timer:是一個實用工具類,該類用來調度一個線程(schedule a thread),使它可以在將來某一時刻執行。 Java的Timer類可以調度一個任務運行一次,或定期循環運行。 Timer tasks should complete quickly. 即定時器中的操作要儘可能花費短的時間。

  java.util.TimerTask:是一個抽象類,它實現了Runnable接口。我們需要擴展該類以便創建自己的TimerTask,這個TimerTask可以被Timer調度。

  注意:默認, task執行線程不是daemon線程, 任務執行完,主線程(或其他啓動定時器的線程)結束時,task線程並沒有結束。如果調用者想要快速終止計時器的任務執行線程,調用者應該調用timer.cancel()方法。

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">timerTest</span>() {
    Timer myTimer = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Timer();
    myTimer.schedule(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TimerTask() {
        @Override
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>() {
            System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"1s後運行"</span>);
            myTimer.cancel(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 需要手動cancel</span>
        }
    }, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1000</span>);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>

2. 源碼解析及案例

  Timer類是線程安全的,多進程不需要外部同步機制就可以共享同一個Timer對象。創建Timer對象,會創建一個java.util.TaskQueue實例,在執行定時任務時,將taskqueue對象作爲鎖,在指定時間間隔添加任務,在任何時刻只能有一個線程執行TimerTask。 
  Timer類使用對象的wait和notify方法來調度任務。

  查看Timer源代碼:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// TaskQueue隊列,內部就是一個TimerTask[]數組</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> TaskQueue queue = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TaskQueue();
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Timer內部維持一個叫TimerThread的線程,傳遞TaskQueue隊列</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> TimerThread thread = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TimerThread(queue);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 創建Timer即啓動線程</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">Timer</span>(String name) {
    thread.setName(name);
    thread.start();<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 啓動線程,後面有分析TimerThread的run方法</span>
}

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">schedule</span>(TimerTask task, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> delay) {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (delay < <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>)
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> IllegalArgumentException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Negative delay."</span>);
    sched(task, System.currentTimeMillis()+delay, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);
}
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 核心調度方法,time表示執行的絕對時間</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">sched</span>(TimerTask task, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> time, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> period) {
    ...
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// timer對象中的queue作爲鎖</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span>(queue) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!thread.newTasksMayBeScheduled)
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> IllegalStateException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Timer already cancelled."</span>);

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span>(task.lock) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (task.state != TimerTask.VIRGIN)
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> IllegalStateException(
                    <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Task already scheduled or cancelled"</span>);
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 設置下次執行的時間</span>
            task.nextExecutionTime = time;
            task.period = period;
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 設置task爲調度狀態</span>
            task.state = TimerTask.SCHEDULED;
        }
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 將當前待執行的task添加到隊列中</span>
        queue.add(task);
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 隊列中取出的head task爲當前task</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (queue.getMin() == task)
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 在任何時刻只能有一個線程執行TimerTask</span>
            queue.notify();
    }
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li></ul>

  其中Timer中啓動的TimerThread的run方法:

<code class="hljs cpp has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> TaskQueue <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>;
TimerThread(TaskQueue <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>) {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span> = <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>;
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> run() {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
         mainLoop();
     } finally {
         <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Someone killed this Thread, behave as if Timer cancelled</span>
         synchronized(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>) {
             newTasksMayBeScheduled = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;
             <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>.clear();  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Eliminate obsolete references</span>
         }
     }
}

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> mainLoop() {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
            TimerTask task;
            boolean taskFired;
            synchronized(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>) {
                <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 如果queue隊列爲空,則將線程阻塞,等待task</span>
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>.isEmpty() && newTasksMayBeScheduled)
                    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>.wait();
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>.isEmpty())
                    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Queue is empty and will forever remain; die</span>

                <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Queue nonempty; look at first evt and do the right thing</span>
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> currentTime, executionTime;
                <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 隊列中獲取task</span>
                task = <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>.getMin();
                synchronized(task.lock) {
                    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 此處說明可以通過cancel()設置終止task的執行,但TimerThread並沒有終止</span>
                    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (task.state == TimerTask.CANCELLED) {
                        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>.removeMin(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 從隊列中移除</span>
                        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">continue</span>;  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// No action required, poll queue again</span>
                    }
                    currentTime = System.currentTimeMillis();
                    executionTime = task.nextExecutionTime;
                    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (taskFired = (executionTime<=currentTime)) {
                        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (task.period == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Non-repeating, remove</span>
                            <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>.removeMin();
                            task.state = TimerTask.EXECUTED;
                        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Repeating task, reschedule</span>
                            <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>.rescheduleMin(
                              task.period<<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> ? currentTime   - task.period
                                            : executionTime + task.period);
                        }
                    }
                }
                <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 還未到執行時間,則等待相應的時間</span>
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!taskFired) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Task hasn't yet fired; wait</span>
                    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">queue</span>.wait(executionTime - currentTime);
            }
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (taskFired)  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Task fired; run it, holding no locks</span>
                task.run(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 執行task的run方法!</span>
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span>(InterruptedException e) {
        }
    }
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li></ul>

  TimerTask的調度流程:由以上分析可知,當我們創建一個Timer時,同時內部創建了一個TimerThread線程,並啓動它,該線程會不斷掃描從Timer傳遞進來的task隊列,如果爲空,則wait()阻塞該線程;當timer調用shedule方法的時候,將傳遞的task添加到隊列中,同時調用queue.notify()方法喚醒TimerThread線程,則從隊列中取出task根據給定的等待時間wait等待,等待完成後執行task.run();啓動任務。(這種結構可以應用到簡單的爬蟲中) 
  這裏寫圖片描述

  如何終止Timer線程: 
  默認情況下,創建的timer線程會一直執行,一般通過下面的方式來終止timer線程:

  1. 調用timer的cancle方法
  2. 把timer線程設置成daemon線程,(new Timer(true)創建daemon線程),在jvm裏,如果所有用戶線程結束,那麼守護線程也會被終止,不過這種方法一般不用。

  下面給出一個Timer執行多個task的簡單案例:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">multileTasksShareOneTimer</span>() {
    Timer myTimer = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Timer();
    TimerTask task1 = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TimerTask() {
        @Override
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>() {
            System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(Thread.currentThread().getName() +<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" 1s後執行task1"</span>);
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//myTimer.cancel();</span>
        }
    };

    TimerTask task2 = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TimerTask() {
        @Override
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>() {
            System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(Thread.currentThread().getName() +<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" 2s後執行task2"</span>);
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//myTimer.cancel();</span>
        }
    };
    myTimer.schedule(task1, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1000</span>);
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// task2.cancel();</span>
    myTimer.schedule(task2, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2000</span>);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li></ul>

  運行輸出:注意此時Timer的TimerThread並沒有結束,因爲在mainLoop()等待task而wait進入阻塞狀態! 
這裏寫圖片描述
  如果設置了task2.cancel();則調度執行task2會拋出異常: 
這裏寫圖片描述

  定時器實際使用的場景還是很多的,比如下面的,在每天零晨備份數據庫,等等。

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * 每天0晨備份數據庫
 */</span>
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@SuppressWarnings</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"deprecation"</span>)
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">backupDatabase</span>() {
    Timer timer = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Timer();
    TimerTask task = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TimerTask() {

        <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>() {
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 備份數據庫</span>
            System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"數據庫備份..."</span>);
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// other operation</span>
        }
    };
    Date firstTime = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Date();
    firstTime.setHours(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);
    firstTime.setMinutes(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);
    firstTime.setSeconds(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> oneDayPeriod = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">24</span> * <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">60</span> * <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">60</span> * <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1000</span>;
    timer.scheduleAtFixedRate(task, firstTime, oneDayPeriod);
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// timer.schedule(task, firstTime, oneDayPeriod);</span>
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li></ul>

  最後:如果是簡單的定時調度,使用Timer就夠了,如果複雜的調度任務,可以考慮使用Quartz

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章