Android線程池

一、為什么使用線程池
1.線程池吃好處
(1).對(duì)多個(gè)線程進(jìn)行統(tǒng)一管理,避免資源競(jìng)爭(zhēng)中出現(xiàn)問(wèn)題
(2).(重點(diǎn))對(duì)線程進(jìn)行復(fù)用,線程在執(zhí)行完任務(wù)后不會(huì)立刻銷毀,而會(huì)等待另外的任務(wù),這樣就不會(huì)頻繁的創(chuàng)建、銷毀線程和調(diào)用GC
2.線程池適用的場(chǎng)景
(1)在項(xiàng)目中頻繁的開(kāi)啟線程,需要多線程去處理不同的任務(wù)
(2)需要監(jiān)控線程的運(yùn)行狀態(tài)
3.線程池運(yùn)行規(guī)則
(1)如果線程池中的數(shù)量未達(dá)到核心線程的數(shù)量,則會(huì)直接啟動(dòng)一個(gè)核心線程來(lái)執(zhí)行任務(wù)
(2)如果線程池中的數(shù)量已經(jīng)達(dá)到或超過(guò)核心線程數(shù),則任務(wù)會(huì)被插入到任務(wù)隊(duì)列中等待執(zhí)行
(3)如果(2)中的任務(wù)無(wú)法插入到任我隊(duì)列中,由于任務(wù)隊(duì)列已滿,這時(shí)候如果線程數(shù)量未達(dá)到線程池規(guī)定的最大值,則會(huì)啟動(dòng)一個(gè)非核心線程來(lái)執(zhí)行
(4)如果(3)中線程數(shù)量已達(dá)到線程池最大值,則會(huì)拒絕執(zhí)行此任務(wù),ThreadPoolExecutor會(huì)調(diào)用RejectedExecutionHandler的rejectedExecution方法通知調(diào)用者
二、線程池的實(shí)現(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: 非核心線程的超時(shí)時(shí)長(zhǎng),當(dāng)系統(tǒng)中非核心線程閑置時(shí)間超過(guò)keepAliveTime之后,則會(huì)被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut屬性設(shè)置為true,則該參數(shù)也表示核心線程的超時(shí)時(shí)長(zhǎng)。

unit: keepAliveTime這個(gè)參數(shù)的單位,有納秒、微秒、毫秒、秒、分、時(shí)、天等。

workQueue: 線程池中的任務(wù)隊(duì)列,該隊(duì)列主要用來(lái)存儲(chǔ)已經(jīng)被提交但是尚未執(zhí)行的任務(wù)。存儲(chǔ)在這里的任務(wù)是由ThreadPoolExecutor的execute方法提交來(lái)的。

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

handler: 拒絕策略,當(dāng)線程無(wú)法執(zhí)行新任務(wù)時(shí)(一般是由于線程池中的線程數(shù)量已經(jīng)達(dá)到最大數(shù)或者線程池關(guān)閉導(dǎo)致的),默認(rèn)情況下,當(dāng)線程池?zé)o法處理新線程時(shí),會(huì)拋出一個(gè)RejectedExecutionException。

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

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        //獲得當(dāng)前線程的生命周期對(duì)應(yīng)的二進(jìn)制狀態(tài)碼
        int c = ctl.get();
        //判斷當(dāng)前線程數(shù)量是否小于核心線程數(shù)量,如果小于就直接創(chuàng)建核心線程執(zhí)行任務(wù),創(chuàng)建成功直接跳出,失敗則接著往下走.
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //判斷線程池是否為RUNNING狀態(tài),并且將任務(wù)添加至隊(duì)列中.
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //審核下線程池的狀態(tài),如果不是RUNNING狀態(tài),直接移除隊(duì)列中
            if (! isRunning(recheck) && remove(command))
                reject(command);
                //如果當(dāng)前線程數(shù)量為0,則單獨(dú)創(chuàng)建線程,而不指定任務(wù).
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //如果不滿足上述條件,嘗試創(chuàng)建一個(gè)非核心線程來(lái)執(zhí)行任務(wù),如果創(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);
        //還是通過(guò)調(diào)用execute
        execute(ftask);
        //最后會(huì)將包裝好的Runable返回
        return ftask;
    }

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

//可以看出FutureTask也是實(shí)現(xiàn)Runnable接口,因?yàn)镽unableFuture本身就繼承了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é)論
從上面兩個(gè)方法的源碼我們可以分析出幾個(gè)結(jié)論,

submit()其實(shí)還是需要調(diào)用execute()去執(zhí)行任務(wù)的,不同是submit()將包裝好的任務(wù)進(jìn)行了返回,他會(huì)返回一個(gè)Future對(duì)象。
從execute()方法中,不難看出addWorker()方法, 是創(chuàng)建線程(核心線程,非核心線程)的主要方法,而reject()方法為線程創(chuàng)建失敗的回調(diào)。
所以,通常情況下,在不需要線程執(zhí)行返回結(jié)果值時(shí),我們使用execute 方法。 而當(dāng)我們需要返回值時(shí),則使用submit方法,他會(huì)返回一個(gè)Future對(duì)象。Future不僅僅可以獲得一個(gè)結(jié)果,他還可以被取消,我們可以通過(guò)調(diào)用future的cancel()方法,取消一個(gè)Future的執(zhí)行。 比如我們加入了一個(gè)線程,但是在這過(guò)程中我們又想中斷它,則可通過(guò)sumbit 來(lái)實(shí)現(xiàn)。
三、Android 中常用的幾種線程池
1.FixedThreadPool (可重用固定線程數(shù))

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

特點(diǎn):參數(shù)為核心線程數(shù),只有核心線程,無(wú)非核心線程,并且阻塞隊(duì)列無(wú)界
創(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("當(dāng)前線程:",Thread.currentThread().getName());
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    };
                    fixedThreadPool.execute(runnable);

                }
            }
        });

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

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

CachedThreadPool中是沒(méi)有核心線程的,但是它的最大線程數(shù)卻為Integer.MAX_VALUE,另外,CachedThreadPool是有線程超時(shí)機(jī)制的,它的超時(shí)時(shí)間為60秒。

由于最大線程數(shù)為無(wú)限大,所以每當(dāng)添加一個(gè)新任務(wù)進(jìn)來(lái)的時(shí)候,如果線程池中有空閑的線程,則由該空閑的線程執(zhí)行新任務(wù);如果沒(méi)有空閑線程,則創(chuàng)建新線程來(lái)執(zhí)行任務(wù)。

根據(jù)CachedThreadPool的特點(diǎn),在有大量耗時(shí)短的任務(wù)請(qǐng)求時(shí),可使用CachedThreadPool,因?yàn)楫?dāng)CachedThreadPool中沒(méi)有新任務(wù)的時(shí)候,它里邊所有的線程都會(huì)因?yàn)?0秒超時(shí)而被終止
創(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é)果:過(guò)2s后直接打印30個(gè)任務(wù)
3.SingleThreadPool(單個(gè)核線的fixed)

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

它的核心線程數(shù)量是固定的,但是非核心線程無(wú)窮大。當(dāng)非核心線程閑置時(shí),則會(huì)被立即回收。
ScheduledThreadPool也是四個(gè)當(dāng)中唯一一個(gè)具有定時(shí)定期執(zhí)行任務(wù)功能的線程池。它適合執(zhí)行一些周期性任務(wù)或者延時(shí)任務(wù)。
創(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打印一個(gè)任務(wù),由于只有一個(gè)核心線程,當(dāng)被占用時(shí),其他的任務(wù)需要進(jìn)入隊(duì)列等待。
四、終止線程池中的某個(gè)線程
一般線程執(zhí)行完run方法之后,線程就正常結(jié)束了,因此有如下幾種方式來(lái)實(shí)現(xiàn):
1.利用 Future 和 Callable
步驟:
實(shí)現(xiàn) Callable 接口
調(diào)用 pool.submit() 方法,返回 Future 對(duì)象
用 Future 對(duì)象來(lái)獲取線程的狀態(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()){ //非阻塞過(guò)程中通過(guò)判斷中斷標(biāo)志來(lái)退出
            try{
              //TODO method(); 
              //阻塞過(guò)程捕獲中斷異常來(lái)退出
            }catch(InterruptedException e){
                e.printStackTrace();
                break;//捕獲到異常之后,執(zhí)行break跳出循環(huán)。
            }
        }
    } 
}

參考: https://blog.csdn.net/qq_22393017/article/details/79356115
http://m.itdecent.cn/p/7b2da1d94b42

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容