java定時線程池_java 定時器線程池(ScheduledThreadPoolExecutor)的實現

前言

定時器線程池提供了定時執行任務的能力,即可以延遲執行,可以周期性執行。但定時器線程池也還是線程池,最底層實現還是ThreadPoolExecutor,可以參考我的另外一篇文章多線程–精通ThreadPoolExecutor。

特點說明

1.構造函數

public ScheduledThreadPoolExecutor(int corePoolSize) {

// 對于其他幾個參數在ThreadPoolExecutor中都已經詳細分析過了,所以這里,將不再展開

// 這里我們可以看到調用基類中的方法時有個特殊的入參DelayedWorkQueue。

// 同時我們也可以發現這里并沒有設置延遲時間、周期等參數入口。

// 所以定時執行的實現必然在DelayedWorkQueue這個對象中了。

super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,

new DelayedWorkQueue());

}

2.DelayedWorkQueue

DelayedWorkQueue是在ScheduledThreadPoolExecutor的一個內部類,實現了BlockingQueue接口

里面存放任務隊列的數組如下:

private RunnableScheduledFuture>[] queue =

new RunnableScheduledFuture>[INITIAL_CAPACITY];

我們分析過ThreadPoolExecutor,它從任務隊列中獲取任務的方式為poll和take兩種,所以看一下poll和take兩個方法的源碼,回顧一下,ThreadPoolExecutor它會調用poll或take方法,先poll,再take,只要其中一個接口有返回就行

public RunnableScheduledFuture> poll() {

final ReentrantLock lock = this.lock;

lock.lock();

try {

RunnableScheduledFuture> first = queue[0];

// 這里有個getDelay,這是關鍵點,獲取執行延時時間

// 但是如果我們有延時設置的話,這就返回空了,然后就會調用take方法

if (first == null || first.getDelay(NANOSECONDS) > 0)

return null;

else

return finishPoll(first);

} finally {

lock.unlock();

}

}

public RunnableScheduledFuture> take() throws InterruptedException {

final ReentrantLock lock = this.lock;

lock.lockInterruptibly();

try {

for (;;) {

RunnableScheduledFuture> first = queue[0];

if (first == null)

available.await();

else {

// 獲取延時時間

long delay = first.getDelay(NANOSECONDS);

if (delay <= 0)

return finishPoll(first);

first = null; // don't retain ref while waiting

if (leader != null)

available.await();

else {

Thread thisThread = Thread.currentThread();

leader = thisThread;

try {

// 使用鎖,執行延時等待。

// 使用鎖,執行延時等待。

// 使用鎖,執行延時等待。

available.awaitNanos(delay);

} finally {

if (leader == thisThread)

leader = null;

}

}

}

}

} finally {

if (leader == null && queue[0] != null)

available.signal();

lock.unlock();

}

}

3.RunnableScheduledFuture

在ScheduledThreadPoolExecutor內部有一個ScheduledFutureTask類實現了RunnableScheduledFuture,ScheduledFutureTask這個類采用了裝飾者設計模式,在執行Runnable的方法基礎上還執行了一些額外的功能。

我們需要特別注意幾個參數period、time。

(1)time

首先看一下time的作用,可以發現time是用于獲取執行延時時間的,也就是delay是根據time生成的

public long getDelay(TimeUnit unit) {

return unit.convert(time - now(), NANOSECONDS);

}

(2)period

這個參數不是說設置執行幾個周期,而是用于判斷是否需要按周期執行,以及執行周期,也就是本次執行與下次執行間隔的時間

// 判斷是否需要按周期執行,如果周期設置成0,不是無間隔執行,而是只執行一次,這個需要特別注意

public boolean isPeriodic() {

return period != 0;

}

private void setNextRunTime() {

long p = period;

if (p > 0)

// 這里將周期加給time,這樣獲取的延遲時間就是周期時間了。

time += p;

else

time = triggerTime(-p);

}

(3)執行

public void run() {

// 先判斷是否為周期性的任務

boolean periodic = isPeriodic();

if (!canRunInCurrentRunState(periodic))

cancel(false);

else if (!periodic)

// 如果不是周期性的,就執行調用父類的run方法,也就是構造函數中傳入的Runnable對象的run方法。

ScheduledFutureTask.super.run();

// 在if的括號中先執行了任務

else if (ScheduledFutureTask.super.runAndReset()) {

// 如果是周期性的,就需要設置下次執行的時間,然后利用reExecutePeriodic方法,將任務再次丟入任務隊列中。

// 這里尤其需要注意的是if中的邏輯執行失敗,如果沒有捕捉異常,那么后面的邏輯就不會再執行了,也就是說中間有一次執行失敗,后面這個周期性的任務就失效了。

setNextRunTime();

reExecutePeriodic(outerTask);

}

}

總結

ScheduledThreadPoolExecutor通過time參數,設置當前任務執行的等待時間,再通過period設置任務下次執行需要等待的時間。這兩個參數都不是設置在線程池中的,而是攜帶在任務中的,這就可以把線程池和任務進行完全解耦。

注意點:

(1)任務的執行等待時間是在隊列的take方法中的。

(2)period參數設置成0,任務將只會執行一次,而不會執行多次

(3)如果要自己實現周期性Task,周期性任務在執行過程中,一定要注意捕捉異常,否則某一次執行失敗,將導致后續的任務周期失效,任務將不再繼續執行。

到此這篇關于java 定時器線程池(ScheduledThreadPoolExecutor)的實現的文章就介紹到這了,更多相關java 定時器線程池內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持腳本之家!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/272851.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/272851.shtml
英文地址,請注明出處:http://en.pswp.cn/news/272851.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

iOS 關于關鍵字高亮

- (NSMutableAttributedString *)colorStr: (NSString *)originalStr // originalStr : 需要高亮傳入的字符串 { NSMutableAttributedString *dataStr [[[NSMutableAttributedString alloc] initWithString:originalStr] autorelease]; for (int i 0; i < originalStr.l…

成功,要“借力”,不要“盡力”(深刻!)

01每個人都喜歡成功&#xff0c;卻又時常感覺自己力不從心一個小男孩在院子里搬一塊石頭&#xff0c;父親在旁邊鼓勵&#xff1a;“孩子&#xff0c;只要你全力以赴&#xff0c;一定搬得起來&#xff01;”但是石頭太重&#xff0c;最終孩子也沒能搬起來。他告訴父親&#xff1…

java 網站開發實例_完整的javaweb項目

【實例簡介】主要功能有以下幾個&#xff1a;1.用戶注冊 2.用戶登錄 3.用戶列表展示 4.用戶信息修改 5.用戶信息刪除【實例截圖】【核心代碼】javaweb└── javaweb├── src│ └── com│ ├── dao│ │ ├── UserDaoImpl.java│ │ └── UserDao.java│…

Nginx+Php-fpm+MySQL+Redis源碼編譯安裝指南

說明&#xff1a;本教程由三部分組成如下&#xff1a; 1. 源碼編譯安裝Nginx 2. 源碼編譯安裝php以及mysql、redis擴展模塊 3. 配置虛擬主機 文中所涉及安裝包程序均提供下載鏈接&#xff0c;歡迎使用 執行環境以及前置條件&#xff1a;Ubuntu 12.04 LTS 已安裝…

NetFramework各個版本的特性筆記

我的博客&#xff1a;http://www.cnblogs.com/hgmyz/p/6916064.html公式記憶&#xff1a;.Net 2.0CLRBCLC#(VB.Net)Win FormWeb Form.Net 3.0.Net 2.0WCFWPFWFWCS.Net 3.5.Net 3.0Asp.Net AjaxSliverlightLinq.Net 4.0 增加了并行的支持&#xff0c;與舊的Framwork并行工作。默…

從0開始學Java——JSPServlet——HttpServletRequest相關的幾個路徑信息

在HttpServletRequest中有幾個獲取路徑的接口&#xff1a;getRequestURI/getContextPath/getServletPath/getPathInfo 這些接口互相之間有什么區別&#xff0c;通過下面這段代碼就可以分辨清楚了&#xff1a; 1 WebServlet("/hello.view")2 public class FirstServle…

C#編譯和運行過程圖例

一張圖&#xff0c;描述C#編譯和運行過程&#xff0c;比較容易記憶理解

java 不重啟部署_編譯Java類后不重啟Tomcat有兩種方式:熱部署、熱加載

不重啟Tomcat有兩種方式&#xff1a;熱部署、熱加載熱部署&#xff1a;容器狀況在運行時重新部署整個項目。這類環境下一般整個內存會清空,重新加載&#xff0c;這類方式有可能會造成sessin丟失等環境。tomcat 6確實可以熱部署了,而且對話也沒丟.熱加載&#xff1a;最好是在調試…

修改mysql的用戶密碼

修改的用戶都以root為列。一、擁有原來的myql的root的密碼&#xff1b; 方法一&#xff1a; #mysql -u root mysql> SET PASSWORD FOR rootlocalhost PASSWORD(newpass); 方法二&#xff1a;在mysql系統外&#xff0c;使用mysqladmin# mysqladmin -u root -p password &quo…

C#中的堆和棧理解

引言&#xff1a;程序運行時&#xff0c;它的數據必須存在內存中&#xff0c;一個數據需要多大內存、存儲在什么地方以及如何存儲都依賴于該數據的數據類型。1、什么是棧棧是一個內存數組&#xff0c;是一個LIFO&#xff08;Last-In-First-Out 后進先出&#xff09;的數據結構。…

java sessionmanager_java.lang.IllegalStateException:沒有SessionManager

你錯過了3件事.Main.javaimport org.eclipse.jetty.server.Server;import org.eclipse.jetty.server.handler.ContextHandler;import org.eclipse.jetty.server.session.HashSessionIdManager;import org.eclipse.jetty.server.session.HashSessionManager;import org.eclipse.…

什么是鏈表

鏈表是一種物理存儲單元上非連續、非順序的存儲結構&#xff0c;數據元素的邏輯順序是通過鏈表中的指針鏈接次序實現的。鏈表由一系列結點&#xff08;鏈表中每一個元素稱為結點&#xff09;組成&#xff0c;結點可以在運行時動態生成。每個結點包括兩個部分&#xff1a;一個是…

C# 基礎:Sealed、new、virtual、abstract、override的理解

目錄 1、sealed 2、new 3、virtual 4、abstract 5、override 1、sealed 密封類不能被繼承&#xff0c;密封方法可以重寫基類中的方法&#xff0c;但其本身不能在任何派生類&#xff08;子類&#xff09;中 進一步重寫。當應用于屬性或者方法時&#xff0c;sealed 修飾符必須始終…

梁興珍 java_數據結構與算法_Java語言

第1章 綜述1.1 數據結構和算法能起到什么作用&#xff1f;1.2 數據結構的概述1.3 算法的概述1.4 一些定義1.5 面向對象編程1.6 軟件工程1.7 對于C程序員的Java1.8 Java數據結構的類庫第2章 數組2.1 Array專題Applet2.2 Java中數組的基礎知識2.3 將程序劃分成類2.4 類接口2.5 Or…

Yii 2.0: yii2-highcharts-widget創建餅狀圖

安裝 The preferred way to install this extension is through composer. 項目根目錄下執行&#xff1a; php composer.phar require --prefer-dist miloschuman/yii2-highcharts-widget "*"或者在composer.json中添加 "miloschuman/yii2-highcharts-widget&qu…

【原創】C#中的抽象類(abstract class)和接口(interface)的比較

在C#中抽象類和接口是兩個相當重要的概念&#xff0c;深入理解對C#程序員是非常必要的&#xff0c;現總結如下&#xff1a;一、抽象類的特點&#xff1a;1、抽象方法只用于方法的聲明并不包含方法的實現&#xff0c;可以看作沒有實現實體的虛方法。2、抽象類不能進行實例化。3、…

U3D 腳本添加和獲得對象

有時候&#xff0c;一開始可能沒有對象&#xff0c;而是由于某種觸發&#xff0c;產生的一個對象&#xff0c;這里講解下&#xff0c;如何通過腳本來創建一個對象&#xff1a; 這是通過腳本創建一個立方體&#xff1a; using UnityEngine; using System.Collections;public cla…

50條超精辟的經典語錄:嘩眾,可以取寵,也可以失寵!

在人生道路上給自己定位很重要&#xff0c;不要苛求自己達到不可能達到的高度。我們能把每一件平凡的事做好就是不平凡&#xff0c;把每一件簡單的事做成功就是不簡單。1.我們只有一個地球&#xff0c;所以你要愛護地球&#xff1b;地球上只有一個我&#xff0c;所以你也要愛護…

java 時間工具類 大于_Java 時間工具類

1 /**2 * 格式化字符串為日期格式3 *4 *paramdateStr 需要格式化的字符串5 *paramformat 需要的日期格式&#xff0c;例如"yyyy-MM-dd HH:mm:ss"6 *return7 */8 public staticDate formatDate(String dateStr, String format) {9 SimpleDateFormat dateFormat newSi…

IP、TCP和DNS與HTTP的密切關系

看了上一篇博文的發表時間&#xff0c;是7月22日&#xff0c;現在是10月22日&#xff0c;已經有三個月沒寫博客了。這三個月里各種忙各種瞎折騰&#xff0c;發生了很多事情&#xff0c;也思考了很多問題。現在這段時間開始閑下來了&#xff0c;同時該思考的事情也思考清楚了&am…