1. 程式人生 > >面試總結(6):ScheduledExecutorService的使用

面試總結(6):ScheduledExecutorService的使用

前言

android的執行緒池主要有四個:

newSingleThreadExecutor:單執行緒池,同時只有一個執行緒在跑。
newCachedThreadPool() :回收型執行緒池,可以重複利用之前建立過的執行緒,執行執行緒最大數是Integer.MAX_VALUE。
newFixedThreadPool() :固定大小的執行緒池,跟回收型執行緒池類似,只是可以限制同時執行的執行緒數量

哎???第四個是什麼來著,不知道你有沒有想起來,反正我是沒記住,結果面試的時候就栽了個跟頭。

正文

第四種執行緒池是ScheduledExecutorService,我平時沒有用過,他的最大優點除了執行緒池的特性以外,可以實現迴圈或延遲任務。

ScheduledExecutorService 和 Timer 的區別

Timer的內部只有一個執行緒,如果有多個任務的話就會順序執行,這樣我們的延遲時間和迴圈時間就會出現問題。

ScheduledExecutorService是執行緒池,所以就不會出現這個情況,在對延遲任務和迴圈任務要求嚴格的時候,就需要考慮使用ScheduledExecutorService了。

ScheduledExecutorService的用法主要有三個:

public class MainActivity extends AppCompatActivity {

    // 通過靜態方法建立ScheduledExecutorService的例項
private ScheduledExecutorService mScheduledExecutorService = Executors.newScheduledThreadPool(4); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 延時任務 mScheduledExecutorService.schedule(threadFactory.newThread(new
Runnable() { @Override public void run() { Log.e("lzp", "first task"); } }), 1, TimeUnit.SECONDS); // 迴圈任務,按照上一次任務的發起時間計算下一次任務的開始時間 mScheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { Log.e("lzp", "first:" + System.currentTimeMillis() / 1000); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }, 1, 1, TimeUnit.SECONDS); // 迴圈任務,以上一次任務的結束時間計算下一次任務的開始時間 mScheduledExecutorService.scheduleWithFixedDelay(new Runnable() { @Override public void run() { Log.e("lzp", "scheduleWithFixedDelay:" + System.currentTimeMillis() / 1000); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }, 1, 1, TimeUnit.SECONDS); } }

在程式碼中已經有註釋說明這三個方法的用法,重點是scheduleAtFixedRate和scheduleWithFixedDelay的區別,下面看具體看一下Log資訊:

首先是scheduleAtFixedRate:

這裡寫圖片描述

從log上看,我們的迴圈任務嚴格按照每一秒發起一次,sleep(3000)對於任務的開啟是沒有影響的,也就是以上一個任務的開始時間 + 延遲時間 = 下一個任務的開始時間。

然後是scheduleWithFixedDelay:

這裡寫圖片描述

從log上看,每一個任務的時間間隔是4秒,而不是我們設定的間隔1秒,任務要耗時3秒,兩個時間相加正好是4秒,那麼之前程式碼註釋的解釋就說的通了:以上一次任務的結束時間 + 延遲時間 = 下一次任務的開始時間。

擴充套件:ThreadFactory

因為線上程池的構造方法中,有ThreadFactory引數,所以就來簡單介紹一個ThreadFactory:

執行緒工廠,可以對傳入的Runnable進行加工操作,典型的工廠模式,需要實現newThread(@NonNull Runnable r) 方法返回加工後的執行緒。

看一下具體的程式碼:

private ThreadFactory threadFactory = new ThreadFactory() {
        @Override
        public Thread newThread(@NonNull final Runnable r) {
            return new Thread() {
                @Override
                public void run() {
                    r.run();
                    Log.e("lzp", "嘿嘿嘿");
                }
            };
        }
    };

例如上面的程式碼,我在引數Runnable物件的run方法後,增加了一句log列印。

然後修改MainActivity的呼叫:

mScheduledExecutorService.schedule(threadFactory.newThread(new Runnable() {
            @Override
            public void run() {
                Log.e("lzp", "first task");
            }
        }), 1, TimeUnit.SECONDS);

這裡面我傳入ThreadFactory加工過後的Runnable,我們推斷,最後應該會列印兩句:first task 和 嘿嘿嘿,執行一下看看是不是符合我們的期望:

這裡寫圖片描述

果然結果沒有讓我們失望。

然後我有了一個想法,我要給執行緒池所有的執行緒都加工一下:

private ScheduledExecutorService mScheduledExecutorService = Executors.newScheduledThreadPool(4, new ThreadFactory() {
        @Override
        public Thread newThread(@NonNull Runnable r) {
           return new Thread(r){
                @Override
                public void run() {
                    Log.e("lzp", "newThread");
                    super.run();
                    Log.e("lzp", "newThread over");
                }
            };
        }
    });

我在Runnable引數的run方法前後增加了列印,於是我迫不及待的運行了app:

這裡寫圖片描述

經過短暫的喜悅,我發現我的希望落空了,出現了兩個問題:

1、Log.e(“lzp”, “newThread over”) 這一句為什麼沒有執行???
2、為什麼ThreadFactory.newThread() 只執行了四次。

帶著疑問我開始檢視原始碼,經過仔細的思考,找到了問題的原因,這裡就簡單解釋一下:

1、執行緒池只會建立最大併發數(corePoolSize)的Worker物件。

2、Worker物件內部包含了Thread和Runnable資訊。

3、新增任務時:
如果Worker沒有到最大值,就建立Worker(通過ThreadFactory的newThread新建了Thread,所以Thread的數量等於Worker數量);
如果已經是最大,就把任務放到佇列(Quene)中。

3、任務執行中:
如果Worker的Runnable不為空,執行Runnable,執行結束,Runnable設定為空;
如果Worker的Runnable為空,從佇列中取出任務執行。

4、Runnable執行結束,執行緒池內部直接退出了執行緒。

弄懂了內部的機制,剛才的問題就全都弄明白了,還發現android的設計者是不希望我們通過這種方法去影響Runnable的實現方法,但是可以去改變執行緒Thread物件的屬性,例如優先順序等等。

總結

這一篇我們先去弄懂了ScheduledExecutorService的特性和用法,然後瞭解了ThreadFactory的用法,最後兩者結合使用,分析並解決可能出現的問題。

由於執行緒池的原始碼還是很多的,這裡就不具體的解釋了,想具體瞭解的朋友可以自己去檢視原始碼或者網上檢視資料。

最後帶上原始碼連結,有問題或者建議可以留言。

相關推薦

面試總結6ScheduledExecutorService的使用

前言 android的執行緒池主要有四個: newSingleThreadExecutor:單執行緒池,同時只有一個執行緒在跑。 newCachedThreadPool() :回收型執行緒池,可以重複利用之前建立過的執行緒,執行執行緒最大數是In

git操作總結6刪除檔案以及刪除檔案後恢復檔案

1.確實要從版本庫中刪除該檔案 (1)那就用命令git rm刪掉 git rm test06add.txt (2)並且git commit: git commit -m "first delete" (3)推送到遠端資料庫 git push 2.刪錯了 (1)

面試總結3執行緒Thread的同步以及sleep() 、wait的區別

前言 這幾天忙一點私事,今天回來趕緊把面試總結接著寫下去,這次來看看Thread的join()方法和sleep()和wait()方法的區別。 正文 執行緒同步 上一篇提到了執行緒同步的問題,主要是通過鎖的形式來進行執行緒間的喚醒和等待,執行緒之間的

軟件架構設計學習總結23軟件架構設計的6大原則

str 軟件架構 edge 程序員 難點 posit not 幫我 mman 1. 單一職責原則(Single Responsibility Principle - SRP) 原文:There should never be more than one reason fo

機器學習總結SVM支援向量機面試必考

基本思想:試圖尋找一個超平面來對樣本分割,把樣本中的正例和反例用超平面分開,並儘可能的使正例和反例之間的間隔最大。 演算法推導過程: (1)代價函式:假設正類樣本y =wTx+ b>=+1,負

老司機帶你玩轉面試6分散式鎖、併發競爭、雙寫一致

![](https://cdn.geekdigging.com/Interview/mianshi_header_1.jpg) ## 前文回顧 建議前面文章沒看過的同學先看下前面的文章: [「老司機帶你玩轉面試(1):快取中介軟體 Redis 基礎知識以及資料持久化」](https://www.geek

linux命令學習6ps命令

bytes 釋放 ice cti width kthread hellip 名稱 pts Linux中的ps命令是Process Status的縮寫。ps命令用來列出系統中當前運行的那些進程。ps命令列出的是當前那些進程的快照,就是執行ps命令的那個時刻的那些進程,如果想要

C++傳智筆記6socket客戶端發送報文接受報文的api接口

內存泄露 rcp 分配內存 strcpy light cpp tac 第三方 _file__ #define _CRT_SECURE_NO_WARNINGS #include "stdio.h" #include "stdlib.h" #include "string.

Windows Phone開發6處理屏幕方向的改變

cati sources mon stack mar ber XML break pac 俺們都知道,智能手機可以通過旋轉手機來改變屏幕的顯示方向,更多的時候,對於屏幕方向的改變,我們要做出相應的處理,例如,當手機屏幕方向從縱向變為橫向時,可能要重新排列頁面上的控件以適應顯

前端面試總結css

pan html元素 內容 brush bre 省略號 import als earlier 表格:Cellspacing:單元格間距,cellpadding:單元格內容之間的空隙,colspan:合並列數,rowspan:合並行數,表頭caption,border-sp

前端面試總結JavaScript

javascrip 類型 作用域鏈 word doc locals session jsonp 作用域 ajax優缺點 json和jsonP區別 省市聯動 全選 數組去重: 如何消除一個數組裏面重復的元素? // 方法一: var arr1 =[1,2,2,2,3,3,3

設計模式六大原則6開閉原則

思考 外部 編程人員 恰恰 單一職責 何事 適應 擴展 分享 開閉原則 定義:一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。 問題由來:在軟件的生命周期內,因為變化、升級和維護等原因需要對軟件原有代碼進行修改時,可能會給舊代碼中引入錯誤,也可能會使我們不得不對

springBoot6web開發-模板引擎jsp

spring boot 一、新建工程 註意新建的工程下沒有webapp目錄eclipse下會自動創建webapp目錄這裏我們需要自動創建一個webapp目錄並創建WEB-INF。 對ServletInitializer.java進行說明 1、這個類相當於我們以前的web.xml 2、只有3.0以上才

學習用Node.js和Elasticsearch構建搜索引擎6實際項目中常用命令使用記錄

nds 黃色 ati cat htm action last shard open 1、檢測集群是否健康。 curl -XGET ‘localhost:9200/_cat/health?v‘#後面加一個v表示讓輸出內容表格顯示表頭 綠色表示一切正常,黃色表示所有

基於MVC4+EasyUI的Web開發框架經驗總結6--在頁面中應用下拉列表的處理

ica new web開發 don ext images 如果 bob 獲取 http://www.cnblogs.com/wuhuacong/p/3840321.html 在很多Web界面中,我們都可以看到很多下拉列表的元素,有些是固定的,有些是動態的;有些是字典內容,

EasyPR源碼剖析6車牌判斷之LBP特征

extend 順序 位置 feature tput ray bpf range str 一、LBP特征 LBP指局部二值模式,英文全稱:Local Binary Pattern,是一種用來描述圖像局部特征的算子,LBP特征具有灰度不變性和旋轉不變性等顯著優點。 原始的LBP

python函數6內置函數和匿名函數

a20 *args -s 執行 code str 思維導圖 inpu 其他 我們學了這麽多關於函數的知識基本都是自己定義自己使用,那麽我們之前用的一些函數並不是我們自己定義的比如說print(),len(),type()等等,它們是哪來的呢? 一、內置函數 由pytho

Tomcat學習總結6——Tomca常用配置詳解

mar evel 代碼段 between 取消 新建 unp -h tom 註:Tomcat 8需要JRE7以上的JRE 1. Tomcat環境變量設置 1.1 Java環境變量設置 右鍵計算機—屬性—高級系統設置—環境變量,在”系統環境變量”,設置如下三個變量(如果變量已

軟件架構設計學習總結13大型網站技術架構網站的可擴展性架構

開放 擴展 修改 restfu 消息發送 封裝 nts 進行 可擴展性 擴展性是指對現有系統影響最小的情況下,系統功能可持續擴展或提升的能力。 設計網站可擴展架構的核心思想是模塊化,並在此基礎上,降低模塊間的耦合性,提供模塊的復用性。模塊通過分布式部署,獨立

軟件架構設計學習總結14大型網站技術架構網站的安全架構

根據 知情 提交 pac 請求參數 用途 text 避免 信息加密 從互聯網誕生起,安全威脅就一直伴隨著網站的發展,各種Web攻擊和信息泄露也從未停止。常見的攻擊手段有XSS攻擊、SQL註入、CSRF、Session劫持等。 1、XSS攻擊 XSS攻擊即跨站點腳本攻擊(C