這篇文章將為大家詳細(xì)講解有關(guān)線程池與定時(shí)任務(wù)如何在Spring中使用,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
創(chuàng)新互聯(lián)長(zhǎng)期為數(shù)千家客戶(hù)提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為安吉企業(yè)提供專(zhuān)業(yè)的成都做網(wǎng)站、網(wǎng)站建設(shè),安吉網(wǎng)站改版等技術(shù)服務(wù)。擁有十年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。
1.功能介紹
Spring框架提供了線程池和定時(shí)任務(wù)執(zhí)行的抽象接口:TaskExecutor和TaskScheduler來(lái)支持異步執(zhí)行任務(wù)和定時(shí)執(zhí)行任務(wù)功能。同時(shí)使用框架自己定義的抽象接口來(lái)屏蔽掉底層JDK版本間以及Java EE中的線程池和定時(shí)任務(wù)處理的差異。
另外Spring還支持集成JDK內(nèi)部的定時(shí)器Timer和Quartz Scheduler框架。
2.線程池的抽象:TaskExecutor
TaskExecutor涉及到的相關(guān)類(lèi)圖如下:
TaskExecutor接口源代碼如下所示:
public interface TaskExecutor extends Executor { /** * Execute the given {@code task}. * <p>The call might return immediately if the implementation uses * an asynchronous execution strategy, or might block in the case * of synchronous execution. * @param task the {@code Runnable} to execute (never {@code null}) * @throws TaskRejectedException if the given task was not accepted */ @Override void execute(Runnable task); }
此接口和Executor幾乎完全一樣,只定義了一個(gè)接收Runnable參數(shù)的方法,據(jù)Spring官方介紹此接口最初是為了在其他組建中使用線程時(shí),將JKD抽離出來(lái)而設(shè)計(jì)的。在Spring的一些其他組件中比如ApplicationEventMulticaster,Quartz都是使用TaskExecutor來(lái)作為線程池的抽象的。
3.Spring提供的TaskExecutor的實(shí)現(xiàn)類(lèi)
org.springframework.core.task.SimpleAsyncTaskExecutor
此實(shí)現(xiàn)支持任務(wù)的異步執(zhí)行,但是此實(shí)現(xiàn)沒(méi)有線程的復(fù)用,每次執(zhí)行一個(gè)提交的任務(wù)時(shí)候都會(huì)新建一個(gè)線程,任務(wù)執(zhí)行完成后會(huì)將線程關(guān)閉,最大并發(fā)數(shù)默認(rèn)是沒(méi)有限制的,但是可以通過(guò)調(diào)用下面的方法來(lái)設(shè)置最大并發(fā)數(shù)。一般使用線程池來(lái)代替此實(shí)現(xiàn),特別是執(zhí)行一些生命周期很短的任務(wù)的時(shí)候。
public void setConcurrencyLimit(int concurrencyLimit) { this.concurrencyThrottle.setConcurrencyLimit(concurrencyLimit); }
Spring還提供了同步任務(wù)執(zhí)行的實(shí)現(xiàn)類(lèi):
org.springframework.core.task.SyncTaskExecutor
此類(lèi)中只有一個(gè)方法,代碼如下:
@Override public void execute(Runnable task) { Assert.notNull(task, "Runnable must not be null"); task.run(); }
此方法中直接調(diào)用傳入的Runable對(duì)象的run方法,因此在執(zhí)行此方法的時(shí)候不會(huì)另外開(kāi)啟新的線程,只是普通的方法調(diào)用,同步執(zhí)行提交的Runable對(duì)象。
Spring有兩個(gè)線程池的實(shí)現(xiàn)類(lèi),分別為:SimpleThreadPoolTaskExecutor和ThreadPoolTaskExecutor,其中當(dāng)我們有Quarts和非Quarts共享同一個(gè)線程池的需求的時(shí)候使用SimpleThreadPoolTaskExecutor,除了這種情況,我們一般是使用
ThreadPoolTaskExecutor,此實(shí)現(xiàn)可以通過(guò)屬性注入來(lái)配置線程池的相關(guān)配置。 ThreadPoolTaskExecutor中屬性注入的源碼如下:此配置可以在運(yùn)行期修改,代碼中修改過(guò)程使用了同步控制。
/** * Set the ThreadPoolExecutor's core pool size. * Default is 1. * <p><b>This setting can be modified at runtime, for example through JMX.</b> */ public void setCorePoolSize(int corePoolSize) { synchronized (this.poolSizeMonitor) { this.corePoolSize = corePoolSize; if (this.threadPoolExecutor != null) { this.threadPoolExecutor.setCorePoolSize(corePoolSize); } } }
4.TaskExecutor使用Demo
首先定義一個(gè)任務(wù)如下所示:
public class DataSimulation implements Runnable{ private HourAverageValueDao hourAverageValueDao; @Override public void run() { Random random = new Random(); AverageValue averageValue = new AverageValue(); averageValue.setAverageVaule(random.nextInt(100)); averageValue.setCreatedTime(new Date()); hourAverageValueDao.insert(averageValue); } }
此任務(wù)中產(chǎn)生一個(gè)隨機(jī)數(shù),并封裝成一個(gè)類(lèi)對(duì)象,并將此數(shù)據(jù)插入到數(shù)據(jù)庫(kù)中。
然后需要定一個(gè)類(lèi),使用TaskExecutor,代碼如下:
public class DataFacotory { private TaskExecutor executor; public TaskExecutor getExecutor() { return executor; } public void setExecutor(TaskExecutor executor) { this.executor = executor; } public void dataFactory(){ for (int i =0; i < 10; i++){ executor.execute(new DataSimulation()); } } }
此類(lèi)中定義了TaskExecutor的屬性,并定一個(gè)方法,此方法中提交10個(gè)任務(wù)到TaskExecutor,下面只需配置Spring文件,注入TaskExecutor就可以實(shí)現(xiàn)線程池的使用。配置文件如下所示:
<bean id = "taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <property name="corePoolSize" value = "5"></property> <property name = "maxPoolSize" value="10"></property> <property name="queueCapacity" value="25"></property> </bean>
完成配置后即可使用此線程池。Spring提供的線程池可以通過(guò)配置文件配置線程池的配置,相比JDk自帶的線程池是一個(gè)很大的優(yōu)勢(shì)。
5.為什么使用線程池
1.通過(guò)使用線程池來(lái)實(shí)現(xiàn)線程的復(fù)用,減少線程創(chuàng)建和銷(xiāo)毀的開(kāi)銷(xiāo)
2.將執(zhí)行線程的任務(wù)交給線程池來(lái)操作,一定意義上實(shí)現(xiàn)了解耦
3.使用線程池可以控制任務(wù)的最大并發(fā)數(shù)目,這個(gè)在防止內(nèi)存溢出以及并發(fā)優(yōu)化方面有很重要的作用。
6.定時(shí)任務(wù)抽象類(lèi):TaskScheduler
TaskScheduler接口源代碼如下:
public interface TaskScheduler { //通過(guò)觸發(fā)器來(lái)決定task是否執(zhí)行 ScheduledFuture schedule(Runnable task, Trigger trigger); //在starttime的時(shí)候執(zhí)行一次 ScheduledFuture schedule(Runnable task, Date startTime); 從starttime開(kāi)始每個(gè)period時(shí)間段執(zhí)行一次task ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period); 每隔period執(zhí)行一次 ScheduledFuture scheduleAtFixedRate(Runnable task, long period); 從startTime開(kāi)始每隔delay長(zhǎng)時(shí)間執(zhí)行一次 ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay); 每隔delay時(shí)間執(zhí)行一次 ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay); }
指定開(kāi)始時(shí)間的接口,如果時(shí)間已經(jīng)是過(guò)去的某一時(shí)間點(diǎn),則此任務(wù)會(huì)馬上執(zhí)行一次。以上幾種執(zhí)行方式傳入Trigger的方式是用的最多的,Trigger接口中只定義了一個(gè)方法:
Date nextExecutionTime(TriggerContext triggerContext);
其中參數(shù)類(lèi)型TriggerContext的定義如下:
public interface TriggerContext { /** * Return the last <i>scheduled</i> execution time of the task, * or {@code null} if not scheduled before. */ Date lastScheduledExecutionTime(); /** * Return the last <i>actual</i> execution time of the task, * or {@code null} if not scheduled before. */ Date lastActualExecutionTime(); /** * Return the last completion time of the task, * or {@code null} if not scheduled before. */ Date lastCompletionTime(); }
提供了獲取上一次任務(wù)執(zhí)行信息的接口。我們通過(guò)實(shí)現(xiàn)Trigger接口可以實(shí)現(xiàn)自定義觸發(fā)器來(lái)執(zhí)行執(zhí)行task。當(dāng)然Spring也提供了兩個(gè)默認(rèn)的實(shí)現(xiàn)類(lèi):PeriodicTrigger和CronTrigger。
7.TaskScheduler定時(shí)任務(wù)Demo
首先在Spring配置文件中啟用注解配置如下:
<task:annotation-driven scheduler="myScheduler"/> //指定scheduler屬性是可選項(xiàng),不添加也可以正常使用 <task:scheduler id="myScheduler" pool-size="10"/>
然后創(chuàng)建service,并在service中使用@Scheduled注解創(chuàng)建定時(shí)任務(wù),代碼如下:
@Component public class SchedulerPoolTest { @Scheduled(cron = "0 * * * * ?") public void task1(){ System.out.println("test"); Thread thread = Thread.currentThread(); System.out.println("ThreadName:" + thread.getName() + ",id:" + thread.getId() + ",group:" + thread.getThreadGroup()); } @Scheduled(fixedDelay = 5000) public void task2(){ System.out.println("test"); Thread thread = Thread.currentThread(); System.out.println("ThreadName:" + thread.getName() + ",id:" + thread.getId() + ",group:" + thread.getThreadGroup()); } }
只是添加以上內(nèi)容可能還不能正常執(zhí)行task,還需要注意以下兩點(diǎn):
1.必須將SchedulerPoolTest類(lèi)包含在spring所掃描的包里面
配置如下:
<context:component-scan base-package="com.zjut.task" />
2.需要在web.xml中添加spring配置文件的監(jiān)聽(tīng)器,代碼如下:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring-task.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
添加以上內(nèi)容后,啟動(dòng)服務(wù)器,將會(huì)定時(shí)執(zhí)行任務(wù)。
8.Cron表達(dá)式
Cron表達(dá)式由6個(gè)字符串組成,每個(gè)字符串分別代表:
{秒} {分} {時(shí)} {日} {月} {周}
其中每個(gè)字符串所允許的取值范圍為:
字段名 允許的值 允許的特殊字符 秒 0-59 , - * / 分 0-59 , - * / 小時(shí) 0-23 , - * / 日 1-31 , - * ? / L W C 月 1-12 or JAN-DEC , - * / 周幾 1-7 or SUN-SAT , - * ? / L C #
常用的Cron表達(dá)式:
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44觸發(fā) "0 0/5 14,18 * * ?" 在每天下午2點(diǎn)到2:55期間和下午6點(diǎn)到6:55期間的每5分鐘觸發(fā) "15-30/5 * * * * ?" 每分鐘的15秒到30秒之間開(kāi)始觸發(fā),每隔5秒觸發(fā)一次 "0 15 10 ? * 5#3" 每個(gè)月第三周的星期四的10點(diǎn)15分0秒觸發(fā)任務(wù)
注:?jiǎn)柼?hào)是用于避免日和周的設(shè)定由沖突而用的,當(dāng)其中一個(gè)設(shè)置了具體的值,另外一個(gè)必須使用?。另外推薦一個(gè)Cron表達(dá)式生成的鏈接:http://www.cronmaker.com/
9.@Async注解
Async注解提供了異步調(diào)用方法的功能,當(dāng)調(diào)用由此注解的方法的時(shí)候方法調(diào)用者會(huì)馬上返回而不會(huì)等待調(diào)用的方法執(zhí)行完成,被調(diào)用的方法會(huì)從線程池中分配一個(gè)線程來(lái)執(zhí)行此方法。
10.Spring定時(shí)任務(wù)中并發(fā)執(zhí)行的問(wèn)題
同一個(gè)任務(wù),當(dāng)上一個(gè)任務(wù)沒(méi)有執(zhí)行完成的時(shí)候,新的任務(wù)不會(huì)執(zhí)行。
關(guān)于線程池與定時(shí)任務(wù)如何在Spring中使用就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。
網(wǎng)頁(yè)標(biāo)題:線程池與定時(shí)任務(wù)如何在Spring中使用
網(wǎng)址分享:http://vcdvsql.cn/article40/peisho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化、App設(shè)計(jì)、網(wǎng)頁(yè)設(shè)計(jì)公司、網(wǎng)站內(nèi)鏈、電子商務(wù)、網(wǎng)站營(yíng)銷(xiāo)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)