Android的線程和線程池

Android的線程和線程池

標簽(空格分隔): android


一些概念:

  • 線程分類:主線程和子線程婴梧,主線程主要處理和界面相關(guān)的事情,子線程用于執(zhí)行耗時操作藕筋。
  • 如果在主線程即ui線程執(zhí)行耗時操作锅锨,會造成ANR竭鞍,即無法響應(yīng)。
  • 一些特殊的線程:AsyncTask(主要用于更新ui)裆熙、IntentService(更方便執(zhí)行后臺任務(wù)端礼,不容易被殺死)、HanderThread入录,AsyncTask底層實現(xiàn)是線程池蛤奥,IntentService、HanderThread底層是線程僚稿。
  • 線程池都可以避免因為頻發(fā)創(chuàng)建和摧毀線程帶來的系統(tǒng)開銷凡桥。

一些特殊的線程:

AsyncTask

  • 定義:AsyncTask是一種輕量級的異步任務(wù)類,它在線程池中執(zhí)行后臺任務(wù)蚀同,執(zhí)行的進度和最終結(jié)果傳遞給主線程并在主線程中更新ui缅刽。
  • AsyncTask的一些方法和參數(shù):
    • 參數(shù):Params表示參數(shù)類型,Progress表示后臺任務(wù)執(zhí)行進度的類型唤崭,而Result表示后臺任務(wù)的返回結(jié)果類型拷恨。
    • 核心方法:
      (1)onPreExecute():在主線程執(zhí)行,可以用于做一些·準備工作谢肾。
      (2)doInBackgroud():在線程池執(zhí)行腕侄,此方法執(zhí)行異步任務(wù),params是異步任務(wù)的參數(shù)芦疏,其中的publishProgress方法會調(diào)用onProgressUpdate方法冕杠,此方法返回結(jié)果給onPostExecute方法。
      (3)onProgressUpdate 在主線程中執(zhí)行酸茴,后臺任務(wù)的執(zhí)行進度發(fā)生變化此方法會被調(diào)用分预。
      (4)onPostExecute 在主線程中執(zhí)行,異步任務(wù)執(zhí)行之后薪捍,此方法會被調(diào)用笼痹。
  • 一個簡單的例子:
/*
自定義的一個AsyncTask類
*/
package com.example.windowtest;

import android.os.AsyncTask;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MyAsyncTask extends AsyncTask<Integer,Integer,String> {
    private TextView txt;
    private ProgressBar pgbar;
    public MyAsyncTask(TextView txt,ProgressBar pgbar){
        super();
        this.txt = txt;
        this.pgbar = pgbar;
    }

    @Override
    protected String doInBackground(Integer... integers) {
        DelayOperator dop = new DelayOperator();
        int i;
        for(i=10;i<=100;i+=10){
            dop.delay();
            publishProgress(i);
        }
        return i+integers[0].intValue()+"";
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        txt.setText("開始執(zhí)行異步線程~");
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        int value = values[0];
        pgbar.setProgress(value);
    }
}
/*
模擬耗時操作的類
*/
package com.example.windowtest;

public class DelayOperator {
    public void delay(){
        try{
           Thread.sleep(1000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}


//主線程中調(diào)用代碼
  btnupdate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MyAsyncTask myAsyncTask = new MyAsyncTask(txt_title,pgbar);
                myAsyncTask.execute(1000);
            }
        });
  
  

運行結(jié)果:


更新進度條
更新進度條
  • AsncTask工作流程
    先上一張我手繪的圖:(這字丑的不說了)


    AsyncTask
    AsyncTask

(1)先從execute方法開始,這個方法實際上就是調(diào)用了executeOnExecutor方法酪穿。
(2)這個executeOnExecutor的方法就做了很多的事情凳干,實現(xiàn)一個線程池,同時onPreExecute方法也被執(zhí)行了被济。
(3)關(guān)于sDefaultExecutor線程池救赐,把AsyncTask的params封裝為FutureTask對象,F(xiàn)utureTask對象又交給線程池的execute方法處理只磷。通過判斷線程池中是否有線程執(zhí)行经磅,如果沒有泌绣,執(zhí)行scheduleNext啟動下一個任務(wù)。
(4)實際上AsyncTask有兩個線程池预厌,一個是用來排隊的SerialExecutor用來排隊阿迈,線程池THREAD_POOL_EXECUTOR執(zhí)行真正的任務(wù),而InternalHandler是用來實現(xiàn)將執(zhí)行環(huán)境切換回主線程配乓。
(5)接下來是切換回主線程的邏輯仿滔,調(diào)用mWorker的call方法,執(zhí)行postResult犹芹,該方法的參數(shù)是doInBackground方法的返回結(jié)果崎页,postResult方法會調(diào)用sHandler發(fā)送消息,切回主線程腰埂。
(6)handleMessage會接受兩種msg.what,一種是(1)POST_RESULT飒焦、(2)POST_PROGRESS,(1)消息標識會執(zhí)行Taskfinish方法(任務(wù)被取消執(zhí)行onCancelled方法,任務(wù)沒有被取消就執(zhí)行onPostExecute方法)屿笼,(2)消息標識會執(zhí)行onProgressUpdate方法牺荠。

想到一個問題,AsyncTask任務(wù)是串行還是并行的驴一?

答:android3.0以下版本是并行的休雌,android3.0以上的版本是串行的,
調(diào)用AsyncTask的executeOnExecutor方法任務(wù)會并行肝断。

代碼驗證:

package com.example.asynctaskdemo;

import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.text.SimpleDateFormat;
import java.util.Date;

public class MainActivity extends AppCompatActivity {
    private Button start_async,intent_service;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        start_async = (Button)findViewById(R.id.start_async_task);
        intent_service = (Button)findViewById(R.id.intent_service);
    }

    public void onClick(View v){
        if(v.getId() == R.id.start_async_task){
            //串行
            new MyAsyncTask("AsyncTask#1").execute("");
            new MyAsyncTask("AsyncTask#2").execute("");
            new MyAsyncTask("AsyncTask#3").execute("");
            new MyAsyncTask("AsyncTask#4").execute("");
            }
            //并行
             if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
                new MyAsyncTask("AsyncTask#5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR," ");
                new MyAsyncTask("AsyncTask#6").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR," ");
                new MyAsyncTask("AsyncTask#7").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR," ");
                new MyAsyncTask("AsyncTask#8").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR," ");
            }
        }else if(v.getId() == R.id.intent_service){
            Intent service = new Intent(this,LocalIntentService.class);
            service.putExtra("task_action","com.example.action.TASK1");
            startService(service);
            service.putExtra("task_action","com.example.action.TASK2");
            startService(service);
            service.putExtra("task_action","com.example.action.TASK3");
            startService(service);
        }
    }
    private static class MyAsyncTask extends AsyncTask<String,Integer,String> {
        private String mName = "AsyncTask";
        private MyAsyncTask(String name){
            super();
            mName = name;
        }

        @Override
        protected String doInBackground(String... strings) {
            try{
                Thread.sleep(3000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            return mName;
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Log.e("haha",s+"execute finish at " +df.format(new Date()));
        }
    }
}

AsyncTask串行
AsyncTask串行
AsyncTask并行
AsyncTask并行

簡單介紹特殊的服務(wù)IntentService

  • 簡單介紹:IntentService的優(yōu)先級比一般的線程的優(yōu)先級高杈曲,它特別適合執(zhí)行高優(yōu)先級的后臺任務(wù)。
  • 第一次啟動:onCreate方法會被調(diào)用胸懈,onCreate方法會創(chuàng)建HandlerThread担扑,然后他的Looper來構(gòu)建一個Handler對象mServiceHandler,mServiceHandler發(fā)送消息趣钱,執(zhí)行HandlerThread的后臺任務(wù)涌献。
  • 每一次啟動IntentService,onStartCommand方法就會調(diào)用一次首有,IntentService在onStartCommand中處理每一個后臺任務(wù)的Intent燕垃。
  • 之后的調(diào)用過程:onStart方法--》(發(fā)送一條消息)mServiceHandler--》(傳遞給onHandleIntent)onHandleIntent --》(調(diào)用結(jié)束后)stopSelf()嘗試停止服務(wù)
  • stopSelf()是立即停止服務(wù),stopSelf(int startId)是等待所有任務(wù)處理完后停止服務(wù)井联。
  • 每執(zhí)行一個后臺任務(wù)就必須啟動一次IntentService利术,而IntentService內(nèi)部則通過消息的方式向HandlerThread請求執(zhí)行任務(wù)。所以IntentService的后臺任務(wù)按順序執(zhí)行低矮。

android中的線程池

三大優(yōu)點

(1)重用線程池中的線程,避免線程創(chuàng)建和線程銷毀帶來的性能開銷
(2)能有效地控制最大的并發(fā)數(shù)被冒,避免大量線程相互搶占資源而導(dǎo)致阻塞現(xiàn)象
(3)能夠簡單地實現(xiàn)線程地管理

配置線程池

public ThreadPoolExecutor(int corePoolSize,//核心線程數(shù)
                          int maxinumPoolSize,//最大線程數(shù)
                          long keepAliveTime,//超時時長
                          TimeUnit unit,//時間單位
                          BlockingQueue<Runnable> workQueue,//線程池的任務(wù)隊列
                          ThreadFactory threadFactory//線程工廠)

關(guān)于線程池執(zhí)行任務(wù)的規(guī)律:(自己寫的口訣)

  • 核心未滿军掂,開核心轮蜕;核心已滿,插入線程池的隊列蝗锥;隊列已滿跃洛,開普通線程;線程已滿终议,通知調(diào)用者汇竭,拋出異常。

幾種常見的線程池

1穴张、FixedThreadPool

  • 全是核心線程细燎,不會被回收
  • 沒有超時機制,更快地響應(yīng)外界請求

2皂甘、CachedThreadPool

  • 適合于大量耗時較少的任務(wù)
  • 只有非核心線程玻驻,有超時機制,會被回收偿枕。
  • 無法存儲元素的隊列(空集合)
  • 線程數(shù)是一個不定的值璧瞬,非常大,可以認為是一個任意的數(shù)渐夸。

3嗤锉、ScheduledThreadPool

  • 執(zhí)行定時任務(wù)和具有固定周期的重復(fù)任務(wù)
  • 核心線程是固定的,非核心線程是沒有限制的墓塌,非核心線程閑置時會被回收

4瘟忱、SingleThreadExecutor

  • 只有一個核心線程,確保所有任務(wù)在一個線程中順序執(zhí)行桃纯。
  • 不需要處理線程同步的問題
private void init(){
        Runnable command = new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(3000);
            }
        };
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
        fixedThreadPool.execute(command);
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        cachedThreadPool.execute(command);
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);
        scheduledThreadPool.schedule(command,2000, TimeUnit.MILLISECONDS);
        scheduledThreadPool.scheduleAtFixedRate(command,10,1000,TimeUnit.MILLISECONDS);
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        singleThreadPool.execute(command);
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末酷誓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子态坦,更是在濱河造成了極大的恐慌盐数,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伞梯,死亡現(xiàn)場離奇詭異玫氢,居然都是意外死亡,警方通過查閱死者的電腦和手機谜诫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門漾峡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人喻旷,你說我怎么就攤上這事生逸。” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵槽袄,是天一觀的道長烙无。 經(jīng)常有香客問我,道長遍尺,這世上最難降的妖魔是什么截酷? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮乾戏,結(jié)果婚禮上迂苛,老公的妹妹穿的比我還像新娘。我一直安慰自己鼓择,他們只是感情好三幻,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惯退,像睡著了一般赌髓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上催跪,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天锁蠕,我揣著相機與錄音,去河邊找鬼懊蒸。 笑死荣倾,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的骑丸。 我是一名探鬼主播舌仍,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼通危!你這毒婦竟也來了铸豁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤菊碟,失蹤者是張志新(化名)和其女友劉穎节芥,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逆害,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡头镊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了魄幕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片相艇。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖纯陨,靈堂內(nèi)的尸體忽然破棺而出坛芽,到底是詐尸還是另有隱情留储,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布靡馁,位于F島的核電站欲鹏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏臭墨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一膘盖、第九天 我趴在偏房一處隱蔽的房頂上張望胧弛。 院中可真熱鬧,春花似錦侠畔、人聲如沸结缚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽红竭。三九已至,卻和暖如春喘落,著一層夾襖步出監(jiān)牢的瞬間茵宪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工瘦棋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留稀火,地道東北人。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓赌朋,卻偏偏與公主長得像凰狞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子沛慢,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 從用途上來說赡若,線程分為主線程和子線程,主線程主要處理和界面相關(guān)的事情团甲,子線程則往往用于執(zhí)行耗時操作逾冬。 除了Thre...
    小柏不是大白閱讀 635評論 0 3
  • 線程 線程在Android中是一個很重要的概念,從用途上來說伐庭,線程分為主線程和子線程粉渠,主線程主要處理和界面相關(guān)的事...
    12313凱皇閱讀 1,368評論 0 1
  • 線程分為主線程和子線程,主線程主要處理和界面相關(guān)的事情圾另,而子線程則往往用于執(zhí)行耗時操作霸株。 AsyncTask封裝了...
    Tom_Ji閱讀 457評論 0 5
  • Android中可以扮演線程的角色還有很多,比如AsyncTask和IntentService集乔,同時Handler...
    空白_c46e閱讀 351評論 0 2
  • 春節(jié)假期去件,幾點感悟心得分享一下坡椒。 1-閱讀的收獲:讀了樊登老師的【讀書是一輩子的事情】第一部份認識自己,這是很多書...
    風之吻Sam閱讀 213評論 0 4