在項目中使用到了多線程定時執行指定任務中發現以下一些問題:
/**
* Creates and executes a periodic action that becomes enabled first
* after the given initial delay, and subsequently with the given
* 創建並執行一個在給定的初始延遲之後才啓用的週期操作,然後就按給定的週期執行,
* period; that is executions will commence after
* 必須在指定任務完成之後開始。
* {@code initialDelay} then {@code initialDelay+period}, then
* {@code initialDelay + 2 * period}, and so on.
* If any execution of the task
* encounters an exception, subsequent executions are suppressed.
* Otherwise, the task will only terminate via cancellation or
* termination of the executor. If any execution of this task
* takes longer than its period, then subsequent executions
* may start late, but will not concurrently execute.
*
* @param command the task to execute
* @param initialDelay the time to delay first execution
* @param period the period between successive executions
* @param unit the time unit of the initialDelay and period parameters
* @return a ScheduledFuture representing pending completion of
* the task, and whose {@code get()} method will throw an
* exception upon cancellation
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
* @throws NullPointerException if command is null
* @throws IllegalArgumentException if period less than or equal to zero
*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
command:執行線程
initialDelay:初始化延時
period:兩次開始執行最小間隔時間
unit:計時單位
對於scheduleAtFixedRate方法,當我們要執行的任務大於我們指定的執行間隔時會怎麼樣呢?
對於中文API中的註釋,我們可能會被忽悠,認爲無論怎麼樣,它都會按照我們指定的間隔進行執行,其實當執行任務的時間大於我們指定的間隔時間時,它並不會在指定間隔時開闢一個新的線程併發執行這個任務。而是等待該線程執行完畢。
/**
* Creates and executes a periodic action that becomes enabled first
* after the given initial delay, and subsequently with the
* 創建並執行一個在給定的初始延遲之後才啓用的週期操作,然後,在一個執行的終止和下一個執行之間的延遲
* given delay between the termination of one execution and the
* commencement of the next. If any execution of the task
* 之間。
* encounters an exception, subsequent executions are suppressed.
* Otherwise, the task will only terminate via cancellation or
* termination of the executor.
*如果任務的任何執行遇到異常,則會抑制隨後的執行。否則,任務將僅通過執行程序的取消或終止而終止。
* @param command the task to execute
* @param initialDelay the time to delay first execution
* @param delay the delay between the termination of one
* execution and the commencement of the next
* @param unit the time unit of the initialDelay and delay parameters
* @return a ScheduledFuture representing pending completion of
* the task, and whose {@code get()} method will throw an
* exception upon cancellation
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
* @throws NullPointerException if command is null
* @throws IllegalArgumentException if delay less than or equal to zero
*/
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
command:執行線程
initialDelay:初始化延時
period:前一次執行結束到下一次執行開始的間隔時間(間隔執行延遲時間)
unit:計時單位
這個是以delay爲固定延遲時間,按照一定的等待時間來執行任務,initialDelay意義與上面的相同。
如果執行初始化延時爲0秒,間隔2秒,如果執行任務用了10秒,那下個任務開始就是第12秒後開始執行。
這個是優先保證任務執行的間隔。
以上是源碼介紹,翻譯不好,希望大家指出。
這裏用三個定時任務說明下問題,到時根據自己需求制定初時時間、間隔時間、以及線程任務數。
package com;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
*
* @author ZL
* 2017年8月29日
*/
public class Schedule {
private static ScheduledExecutorService excutor = Executors.newSingleThreadScheduledExecutor();
/**
* scheduleAtFixedRate
* 按指定頻率週期執行某個任務 <br>
* 初始化延遲0ms開始執行,每隔2ms重新執行一次任務。
*/
public void one() {
excutor.scheduleAtFixedRate(new EchoServerone(), // 執行線程
0, // 初始化延遲
2000, // 兩次開始的執行的最小時間間隔
TimeUnit.MILLISECONDS // 計時單位
);
}
/**
* scheduleAtFixedRate
* 按指定頻率週期執行某個任務 <br>
* 初始化延遲0ms開始執行,每隔2ms重新執行一次任務。
*/
public void two() {
excutor.scheduleAtFixedRate(new EchoServertwo(), // 執行線程
0, // 初始化延遲
2000, // 兩次開始的執行的最小時間間隔
TimeUnit.MILLISECONDS // 計時單位
);
}
/**
* scheduleWithFixedDelay
* 按指定頻率週期執行某個任務 <br>
* 初始化延遲0ms開始執行,每隔2ms重新執行一次任務。
*/
public void three() {
excutor.scheduleWithFixedDelay(new EchoServerthree(), // 執行線程
0, // 初始化延遲
2000, // 前一次執行結束到下一次執行開始的間隔時間
TimeUnit.MILLISECONDS);
}
public static void main(String[] args) {
Schedule schedule = new Schedule();
schedule.one();
schedule.two();
schedule.three();
}
}
package com;
/**
* 線程任務一
* @author ZL
* 2017年8月29日
*/
public class EchoServerone implements Runnable {
@Override
public void run() {
System.out.println("線程任務一開始執行"+DateUtils.getnewTime());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("線程任務一執行結束"+DateUtils.getnewTime());
}
}
package com;
/**
* 線程任務二
* @author ZL
* 2017年8月29日
*/
public class EchoServertwo implements Runnable {
@Override
public void run() {
System.out.println("線程任務二開始執行"+DateUtils.getnewTime());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("線程任務二 執行結束"+DateUtils.getnewTime());
}
}
package com;
/**
* 線程任務三
* @author ZL
* 2017年8月29日
*/
public class EchoServerthree implements Runnable {
@Override
public void run() {
System.out.println("線程任務三開始執行"+DateUtils.getnewTime());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("線程任務三執行結束"+DateUtils.getnewTime());
}
}
運行結果:
線程任務一開始執行2017-08-29 17:07:25
線程任務一執行結束2017-08-29 17:07:35
線程任務二開始執行2017-08-29 17:07:35
線程任務二 執行結束2017-08-29 17:07:45
線程任務三開始執行2017-08-29 17:07:45
線程任務三執行結束2017-08-29 17:07:55
線程任務一開始執行2017-08-29 17:07:55
線程任務一執行結束2017-08-29 17:08:05
線程任務二開始執行2017-08-29 17:08:05
線程任務二 執行結束2017-08-29 17:08:15
線程任務一開始執行2017-08-29 17:08:15
線程任務一執行結束2017-08-29 17:08:25
線程任務二開始執行2017-08-29 17:08:25
線程任務二 執行結束2017-08-29 17:08:35
線程任務一開始執行2017-08-29 17:08:35
線程任務一執行結束2017-08-29 17:08:45
線程任務二開始執行2017-08-29 17:08:45
線程任務二 執行結束2017-08-29 17:08:55
線程任務一開始執行2017-08-29 17:08:55
看到運行結果肯定會發現,定時任務三隻在初次運行時執行後由於每個線程執行中都會有10秒的休眠,,然後定時任務三就不運行了,三個定時任務的間隔時間都爲兩秒。
解決辦法是:
修改的代碼:
/**
* 把scheduleWithFixedDelay換成scheduleAtFixedRate
* 按指定頻率週期執行某個任務 <br>
* 初始化延遲0ms開始執行,每隔2ms重新執行一次任務。
*/
public void three() {
excutor.scheduleAtFixedRate(new EchoServerthree(), // 執行線程
0, // 初始化延遲
2000, // 前一次執行結束到下一次執行開始的間隔時間
TimeUnit.MILLISECONDS);
}
運行結果:
線程任務一開始執行2017-08-29 17:29:01
線程任務一執行結束2017-08-29 17:29:11
線程任務二開始執行2017-08-29 17:29:11
線程任務二 執行結束2017-08-29 17:29:21
線程任務三開始執行2017-08-29 17:29:21
線程任務三執行結束2017-08-29 17:29:31
線程任務一開始執行2017-08-29 17:29:31
線程任務一執行結束2017-08-29 17:29:41
線程任務二開始執行2017-08-29 17:29:41
線程任務二 執行結束2017-08-29 17:29:51
線程任務三開始執行2017-08-29 17:29:51
線程任務三執行結束2017-08-29 17:30:01
線程任務一開始執行2017-08-29 17:30:01
線程任務一執行結束2017-08-29 17:30:11
線程任務二開始執行2017-08-29 17:30:11
線程任務二 執行結束2017-08-29 17:30:21
線程任務三開始執行2017-08-29 17:30:21
線程任務三執行結束2017-08-29 17:30:31
線程任務一開始執行2017-08-29 17:30:31
線程任務一執行結束2017-08-29 17:30:41
線程任務二開始執行2017-08-29 17:30:41
線程任務二 執行結束2017-08-29 17:30:51
這樣運行後的結果就不會出現有線程任務不執行的後果了。需要那種根據自己需求吧。