android編程開發教程之線程池

android編程開發教程之線程池。

為什麼要使用線程池

當同時並發多個網絡線程時,引入線程池技術會極大地提高APP的性能。
顯著減少瞭創建線程的數目。
防止內存過度消耗。控制活動線程的數量,防止並發線程過多。 
使用條件:假設在一臺APP完成一項任務的時間為T 
T1 創建線程的時間
T2 在線程中執行任務的時間,包括線程間同步所需時間
T3 線程銷毀的時間
顯然T = T1+T2+T3。註意這是一個極度簡化的假設。可以看出T1,T3是多線程本身的帶來的開銷,我們渴望減少T1,T3所用的時間,從而減少T的時間。但一些線程的使用者並沒有註意到這一點,所以在程序中頻繁的創建或銷毀線程,這導致T1和T3在T中占有相當比例。顯然這是突出瞭線程的弱點(T1,T3),而不是優點(並發性)。

線程池技術正是關註如何縮短或調整T1,T3時間的技術,從而提高APP程序性能的。它把T1,T3分別安排在服務器程序的啟動和結束的時間段或者一些空閑的時間段,這樣在服務器程序處理客戶請求時,不會有T1,T3的開銷瞭。

四種線程池各自的特點

newCachedThreadPool() 
緩存型池子,先查看池中有沒有以前建立的線程,如果有,就reuse.如果沒有,就建一個新的線程加入池中。能reuse的線程,必須是timeout IDLE內的池中線程,缺省timeout是60s,超過這個IDLE時長,線程實例將被終止及移出池。緩存型池子通常用於執行一些生存期很短的異步型任務 。
newFixedThreadPool() 
fixedThreadPool與cacheThreadPool差不多,也是能reuse就用,但不能隨時建新的線程 其獨特之處:任意時間點,最多隻能有固定數目的活動線程存在,此時如果有新的線程要建立,隻能放在另外的隊列中等待,直到當前的線程中某個線程終止直接被移出池子。和cacheThreadPool不同:fixedThreadPool池線程數固定,但是0秒IDLE(無IDLE)。這也就意味著創建的線程會一直存在。所以fixedThreadPool多數針對一些很穩定很固定的正規並發線程,多用於服務器。
newScheduledThreadPool() 
調度型線程池。這個池子裡的線程可以按schedule依次delay執行,或周期執行 。0秒IDLE(無IDLE)。
SingleThreadExecutor 
單例線程,任意時間池中隻能有一個線程 。用的是和cache池和fixed池相同的底層池,但線程數目是1-1,0秒IDLE(無IDLE)。
從一個Demo開始

package com.example.executortest;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class MainActivity extends Activity implements View.OnClickListener {

    private static final String TAG = "Executor";

    /** 總共多少任務(根據CPU個數決定創建活動線程的個數,這樣取的好處就是可以讓手機承受得住) */
    // private static final int count = Runtime.getRuntime().availableProcessors() * 3 + 2;

    /** 總共多少任務 */
    private static final int count = 3;

    /** 所有任務都一次性開始的線程池  */
    private static ExecutorService mCacheThreadExecutor = null;

    /** 每次執行限定個數個任務的線程池 */
    private static ExecutorService mFixedThreadExecutor = null;

    /** 創建一個可在指定時間裡執行任務的線程池,亦可重復執行 */
    private static ScheduledExecutorService mScheduledThreadExecutor = null;

    /** 每次隻執行一個任務的線程池 */
    private static ExecutorService mSingleThreadExecutor = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initExecutorService();
        Log.i(TAG, "onCreate");

    }

    private void initExecutorService() {
        mCacheThreadExecutor = Executors.newCachedThreadPool();// 一個沒有限制最大線程數的線程池
        mFixedThreadExecutor = Executors.newFixedThreadPool(count);// 限制線程池大小為count的線程池
        mScheduledThreadExecutor = Executors.newScheduledThreadPool(count);// 一個可以按指定時間可周期性的執行的線程池
        mSingleThreadExecutor = Executors.newSingleThreadExecutor();// 每次隻執行一個線程任務的線程池
    }

    private void initView() {
        findViewById(R.id.mCacheThreadExecutorBtn).setOnClickListener(this);
        findViewById(R.id.mFixedThreadExecutorBtn).setOnClickListener(this);
        findViewById(R.id.mScheduledThreadExecutorBtn).setOnClickListener(this);
        findViewById(R.id.mSingleThreadExecutorBtn).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.mCacheThreadExecutorBtn:
                ExecutorServiceThread(mCacheThreadExecutor);
                break;
            case R.id.mFixedThreadExecutorBtn:
                ExecutorServiceThread(mFixedThreadExecutor);
                break;
            case R.id.mScheduledThreadExecutorBtn:
                ExecutorScheduleThread(mScheduledThreadExecutor);
                break;
            case R.id.mSingleThreadExecutorBtn:
                ExecutorServiceThread(mSingleThreadExecutor);
                break;
        }
    }

    private void ExecutorServiceThread(ExecutorService executorService) {
        for (int i = 0; i < 9; ++i) {
            final int index = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2 * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    Log.i(TAG, "Thread:" + Thread.currentThread().getId() + " activeCount:" + Thread.activeCount() + " index:" + index);
                }
            });
        }
    }

    private void ExecutorScheduleThread(ScheduledExecutorService scheduledExecutorService) {
        for (int i = 0; i < 9; ++i) {
            final int index = i;
            scheduledExecutorService.schedule(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2 * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    Log.i(TAG, "Thread:" + Thread.currentThread().getId() + " activeCount:" + Thread.activeCount() + " index:" + index);
                }
            },2, TimeUnit.SECONDS);
        }
    }
}


60s之內點擊兩次mCacheThreadExecutorBtn,60s後再次點擊

mCacheThreadExecutor 
從代碼中我們可以知道,每次點擊輸出9條Log。由強迫癥的同學可能會問為什麼是9條而不是整數10,我會告訴你:因為我願意!

一共三次點擊,一大段空格和光標為分界線。仔細對比可以發現:第一次點擊開啟瞭9個線程。沒有復用任何線程,60s內第二次點擊,全部復用瞭第一次點擊開啟的線程。60s後第三次點擊,由於IDLE機制,原來開啟的線程被自動終止,重新開啟瞭9個新線程。

點擊mFixedThreadExecutorBtn,60s後再次點擊

mFixedThreadExecutor 
兩次點擊,光標為分割線。代碼中可以看到:mFixedThreadExecutor = Executors.newFixedThreadPool(3);設置線程池的最大數量為3。看到這裡有強迫癥的小夥伴是不是突然覺得上文9條Log的9是不是爽瞭一些。畢竟是3的整數倍。從輸出的log可以看出第一次創建瞭125、126、127三個線程。而在這之後,無論多長時間再次點擊mFixedThreadExecutorBtn,都在復用已經創建瞭的3個線程。0秒IDLE(無IDLE)。

點擊mScheduledThreadExecutorBtn,60s後再次點擊

mScheduledThreadExecutor 
兩次點擊,光標為分割…割…割線。這個和mFixedThreadExecutor一樣,區別是首次創建Thread有個啟動延遲時間,本Demo是2s。0秒IDLE(無IDLE)

點擊mSingleThreadExecutorBtn,60s後再次點擊

mSingleThreadExecutor 
兩次點擊,光標為分割線。Log顯示這種線程池從創建122線程之後就一直復用這個線程。心疼newSingleThreadExecutor線程池,身為一個“池”,居然隻能裝下一個線程。唯一的用處是保證所有任務按照FIFO(First In First Out)順序執行。

You May Also Like