Android線程池

一谋减、為什么使用線程池
1.線程池吃好處
(1).對多個線程進行統(tǒng)一管理牡彻,避免資源競爭中出現(xiàn)問題
(2).(重點)對線程進行復用,線程在執(zhí)行完任務后不會立刻銷毀逃顶,而會等待另外的任務讨便,這樣就不會頻繁的創(chuàng)建、銷毀線程和調(diào)用GC
2.線程池適用的場景
(1)在項目中頻繁的開啟線程以政,需要多線程去處理不同的任務
(2)需要監(jiān)控線程的運行狀態(tài)
3.線程池運行規(guī)則
(1)如果線程池中的數(shù)量未達到核心線程的數(shù)量霸褒,則會直接啟動一個核心線程來執(zhí)行任務
(2)如果線程池中的數(shù)量已經(jīng)達到或超過核心線程數(shù),則任務會被插入到任務隊列中等待執(zhí)行
(3)如果(2)中的任務無法插入到任我隊列中盈蛮,由于任務隊列已滿废菱,這時候如果線程數(shù)量未達到線程池規(guī)定的最大值,則會啟動一個非核心線程來執(zhí)行
(4)如果(3)中線程數(shù)量已達到線程池最大值,則會拒絕執(zhí)行此任務殊轴,ThreadPoolExecutor會調(diào)用RejectedExecutionHandler的rejectedExecution方法通知調(diào)用者
二衰倦、線程池的實現(xiàn)ThreadPoolExecutor
1.構(gòu)造方法

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                    BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)

參數(shù)的含義:
corePoolSize: 線程池中核心線程的數(shù)量。

maximumPoolSize: 線程池中最大線程數(shù)量旁理。

keepAliveTime: 非核心線程的超時時長樊零,當系統(tǒng)中非核心線程閑置時間超過keepAliveTime之后,則會被回收孽文。如果ThreadPoolExecutor的allowCoreThreadTimeOut屬性設(shè)置為true驻襟,則該參數(shù)也表示核心線程的超時時長。

unit: keepAliveTime這個參數(shù)的單位芋哭,有納秒沉衣、微秒、毫秒减牺、秒豌习、分、時拔疚、天等肥隆。

workQueue: 線程池中的任務隊列,該隊列主要用來存儲已經(jīng)被提交但是尚未執(zhí)行的任務草雕。存儲在這里的任務是由ThreadPoolExecutor的execute方法提交來的巷屿。

threadFactory: 為線程池提供創(chuàng)建新線程的功能固以,這個我們一般使用默認即可墩虹。

handler: 拒絕策略,當線程無法執(zhí)行新任務時(一般是由于線程池中的線程數(shù)量已經(jīng)達到最大數(shù)或者線程池關(guān)閉導致的)憨琳,默認情況下诫钓,當線程池無法處理新線程時,會拋出一個RejectedExecutionException篙螟。

2.兩個執(zhí)行的方法
(1)execute()方法

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        //獲得當前線程的生命周期對應的二進制狀態(tài)碼
        int c = ctl.get();
        //判斷當前線程數(shù)量是否小于核心線程數(shù)量,如果小于就直接創(chuàng)建核心線程執(zhí)行任務,創(chuàng)建成功直接跳出,失敗則接著往下走.
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //判斷線程池是否為RUNNING狀態(tài),并且將任務添加至隊列中.
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //審核下線程池的狀態(tài),如果不是RUNNING狀態(tài),直接移除隊列中
            if (! isRunning(recheck) && remove(command))
                reject(command);
                //如果當前線程數(shù)量為0,則單獨創(chuàng)建線程,而不指定任務.
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //如果不滿足上述條件,嘗試創(chuàng)建一個非核心線程來執(zhí)行任務,如果創(chuàng)建失敗,調(diào)用reject()方法.
        else if (!addWorker(command, false))
            reject(command);
    }

(2)submit()方法

public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        //還是通過調(diào)用execute
        execute(ftask);
        //最后會將包裝好的Runable返回
        return ftask;
    }

//將Callable<T> 包裝進FutureTask中
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }

//可以看出FutureTask也是實現(xiàn)Runnable接口,因為RunableFuture本身就繼承了Runnabel接口
public class FutureTask<V> implements RunnableFuture<V> {
    .......
}

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

(3)結(jié)論
從上面兩個方法的源碼我們可以分析出幾個結(jié)論菌湃,

submit()其實還是需要調(diào)用execute()去執(zhí)行任務的,不同是submit()將包裝好的任務進行了返回,他會返回一個Future對象遍略。
從execute()方法中,不難看出addWorker()方法, 是創(chuàng)建線程(核心線程,非核心線程)的主要方法,而reject()方法為線程創(chuàng)建失敗的回調(diào)惧所。
所以,通常情況下绪杏,在不需要線程執(zhí)行返回結(jié)果值時下愈,我們使用execute 方法。 而當我們需要返回值時蕾久,則使用submit方法势似,他會返回一個Future對象。Future不僅僅可以獲得一個結(jié)果,他還可以被取消履因,我們可以通過調(diào)用future的cancel()方法障簿,取消一個Future的執(zhí)行。 比如我們加入了一個線程栅迄,但是在這過程中我們又想中斷它站故,則可通過sumbit 來實現(xiàn)。
三毅舆、Android 中常用的幾種線程池
1.FixedThreadPool (可重用固定線程數(shù))

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

特點:參數(shù)為核心線程數(shù),只有核心線程污淋,無非核心線程寸爆,并且阻塞隊列無界
創(chuàng)建

//創(chuàng)建fixed線程池

  final ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);

使用

 /**
     * fixed線程池
     */
        mFixedPoolThread.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                for(int i = 0;i<30;i++){
                    final int finali = i;
                    Runnable runnable = new Runnable() {
                        @Override
                        public void run() {
                            try {
                                Thread.sleep(2000);
                                Log.d("Thread", "run: "+finali);
                                Log.d("當前線程:",Thread.currentThread().getName());
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    };
                    fixedThreadPool.execute(runnable);

                }
            }
        });

結(jié)果為每2s打印5次任務,跟上面的基礎(chǔ)線程池類似
2.CachedThreadPool

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

CachedThreadPool中是沒有核心線程的魔种,但是它的最大線程數(shù)卻為Integer.MAX_VALUE,另外安拟,CachedThreadPool是有線程超時機制的,它的超時時間為60秒。

由于最大線程數(shù)為無限大顾瞻,所以每當添加一個新任務進來的時候王浴,如果線程池中有空閑的線程,則由該空閑的線程執(zhí)行新任務钞啸;如果沒有空閑線程,則創(chuàng)建新線程來執(zhí)行任務。

根據(jù)CachedThreadPool的特點蹬敲,在有大量耗時短的任務請求時,可使用CachedThreadPool,因為當CachedThreadPool中沒有新任務的時候阱扬,它里邊所有的線程都會因為60秒超時而被終止
創(chuàng)建

//創(chuàng)建Cached線程池

 final ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

使用

 /**
    * cached線程池
    */
        mCachedPoolThread.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                for(int i = 0;i<30;i++){
                    final int finali = i;
                    Runnable runnable = new Runnable() {
                        @Override
                        public void run() {
                            try {
                                Thread.sleep(2000);
                                Log.d("Thread", "run: "+finali);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    };
                    cachedThreadPool.execute(runnable);

                }
            }
        });

結(jié)果:過2s后直接打印30個任務
3.SingleThreadPool(單個核線的fixed)

public ScheduledThreadPoolExecutor(int corePoolSize) {  
    super(corePoolSize, Integer.MAX_VALUE,  
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,  
          new DelayedWorkQueue());  
} 

它的核心線程數(shù)量是固定的渠退,但是非核心線程無窮大。當非核心線程閑置時恰梢,則會被立即回收。
ScheduledThreadPool也是四個當中唯一一個具有定時定期執(zhí)行任務功能的線程池。它適合執(zhí)行一些周期性任務或者延時任務。
創(chuàng)建

//創(chuàng)建Single線程池
 final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

使用

/**
    * single線程池
    */
        mSinglePoolExecute.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                for(int i = 0;i<30;i++){
                    final int finali = i;x
                    Runnable runnable = new Runnable() {
                        @Override
                        public void run() {
                            try {
                                Thread.sleep(2000);
                                Log.d("Thread", "run: "+finali);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    };
                    singleThreadExecutor.execute(runnable);

                }
            }
        });

結(jié)果:每2s打印一個任務定续,由于只有一個核心線程,當被占用時,其他的任務需要進入隊列等待峭状。
四、終止線程池中的某個線程
一般線程執(zhí)行完run方法之后逼争,線程就正常結(jié)束了,因此有如下幾種方式來實現(xiàn):
1.利用 Future 和 Callable
步驟:
實現(xiàn) Callable 接口
調(diào)用 pool.submit() 方法胆敞,返回 Future 對象
用 Future 對象來獲取線程的狀態(tài)杂伟。

private void cancelAThread() {
        ExecutorService pool = Executors.newFixedThreadPool(2);
          
          Callable<String> callable = new Callable<String>() {
              
            @Override
            public String call() throws Exception {
                System.out.println("test");
                return "true";
            }
        };
          
        Future<String> f = pool.submit(callable);
          
        System.out.println(f.isCancelled());
        System.out.println(f.isDone());
        f.cancel(true);
  
    }

2.利用 volatile 關(guān)鍵字灵迫,設(shè)置退出flag, 用于終止線程

public class ThreadSafe extends Thread {
    public volatile boolean isCancel = false; 
        public void run() { 
        while (!isCancel){
           //TODO method(); 
        }
    } 
}

3.interrupt()方法終止線程,并捕獲異常

public class ThreadSafe extends Thread {
  
   @Override
    public void run() { 
        while (!isInterrupted()){ //非阻塞過程中通過判斷中斷標志來退出
            try{
              //TODO method(); 
              //阻塞過程捕獲中斷異常來退出
            }catch(InterruptedException e){
                e.printStackTrace();
                break;//捕獲到異常之后嫌术,執(zhí)行break跳出循環(huán)。
            }
        }
    } 
}

參考: https://blog.csdn.net/qq_22393017/article/details/79356115
http://www.reibang.com/p/7b2da1d94b42

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末度气,一起剝皮案震驚了整個濱河市适荣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌院领,老刑警劉巖弛矛,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異比然,居然都是意外死亡丈氓,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門强法,熙熙樓的掌柜王于貴愁眉苦臉地迎上來万俗,“玉大人,你說我怎么就攤上這事饮怯∪蛲幔” “怎么了?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵蓖墅,是天一觀的道長库倘。 經(jīng)常有香客問我,道長置媳,這世上最難降的妖魔是什么于樟? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮拇囊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘靶橱。我一直安慰自己寥袭,他們只是感情好路捧,可當我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著传黄,像睡著了一般杰扫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上膘掰,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天章姓,我揣著相機與錄音,去河邊找鬼识埋。 笑死凡伊,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的窒舟。 我是一名探鬼主播系忙,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼惠豺!你這毒婦竟也來了银还?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤洁墙,失蹤者是張志新(化名)和其女友劉穎蛹疯,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體热监,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡苍苞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了狼纬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刃唐。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡宠叼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情封断,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布碑诉,位于F島的核電站携取,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏柠贤。R本人自食惡果不足惜香浩,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望臼勉。 院中可真熱鬧邻吭,春花似錦、人聲如沸宴霸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至畸写,卻和暖如春驮瞧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背枯芬。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工论笔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人千所。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓狂魔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親真慢。 傳聞我的和親對象是個殘疾皇子毅臊,可洞房花燭夜當晚...
    茶點故事閱讀 44,654評論 2 354