android asyncTask多線程優(yōu)化

AsyncTask的線程優(yōu)化,我們先了解線程和它在java中的怎么使用的。然后分析android中的實現(xiàn)方法。在模擬實驗存在的問題。給出解決方法。

1.線程

單線程只有一個順序執(zhí)行流,多線程則可以包括多個順序執(zhí)行,多個順序流之間互不干擾。

1.1進程

進程是處于運行過程中的程序,并具有一定獨立功能,是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位。

特征:

  • 獨立性:進程是系統(tǒng)中獨立存在的實體,可以擁有自己獨立的資源,每個進程都有自己私有的地址空間(獨立的代碼和數(shù)據(jù)空間)。但是進程間的切換會有較大的開銷。
  • 動態(tài)性:進程是一個正在系統(tǒng)中動態(tài)指令集合。并在進程中加入了時間概念。進程具有自己的生命周期和各種不同的狀態(tài)。
  • 并發(fā)性:多個進程可以在單個處理器上并發(fā)執(zhí)行,多個線程之間不會互相影響。并行指同一時刻,有多條指令在多個處理器上同時執(zhí)行。并發(fā)指在同一時刻,只能有一條指令執(zhí)行,但多個進程,使得在宏觀上具有多個進程同時執(zhí)行的效果。

1.2線程

線程也被稱作輕量級進程。線程是進程的執(zhí)行單元。就像進程在操作系統(tǒng)中的地位一樣,線程在程序中是獨立的,并發(fā)的執(zhí)行流。當進程被初始化之后,主線程就被創(chuàng)建了。通常一個程序只有一個主進程,但我們也可以在該進程內(nèi)創(chuàng)建多條順序執(zhí)行流,這些順序執(zhí)行流就是Thread,每條Thread也是互相獨立的。

  • 一個線程可以擁有自己的堆,棧,自己的程序計算器和自己的局部變量,但不再擁有系統(tǒng)資源,它與父進程的其他線程共享該進程所有的全部資源。
  • 多個進程共享父進程全部資源,因此我們必須確保一個進程不會妨礙同一進程的其他線程。
  • 線程是獨立運行的,它并不知道進程中是否還有其他線程的存在。線程的運行是搶占式。當前運行的進程在任何時候都可能被掛起,以便另一個線程可以運行。
  • 一個線程可以創(chuàng)建和撤銷另一個線程,同一個進程(Process)的多個線程(Thread)之間可以并發(fā)執(zhí)行。

2.Thread和Runnable

2.1 Thread創(chuàng)建和啟動

  • 1.定義子Thread并重寫run()。
  • 2.創(chuàng)建線程對象
  • 3.線程對象用start方法啟動線程
 static class FirstThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println(this.getName() + " " + i);
            }
        }
    }

2.2 實現(xiàn)Runnable接口創(chuàng)建線程類

1.定義實現(xiàn)Runnable接口的類,重寫run方法

public class SecondThread implements Runnable

2.創(chuàng)建Runnable實現(xiàn)類的對象,并以此作為Thread的target來創(chuàng)建Thread對象,這個Thread對象才是真正的線程對象。

 SecondThread st = new SecondThread();

3.調(diào)用線程對象的start方法來啟動該線程

 new Thread(st,"TXB").start();

2.3 Thread和Runnable比較

優(yōu)缺點 繼承Thread 實現(xiàn)Runnable
優(yōu)點 簡單,直接使用this.getName()來獲取當前線程(因為本身是一個線程類對象) 1.只是實現(xiàn)了Runnable接口,還可以繼承其他類2.多個線程共享一個target對象,非常適合多個線程來處理同一份資源的情況
缺點 因為線程類已經(jīng)繼承了Thread,所以不能再繼承其他父類了 略微復雜,要使用Thread.currentThread()來獲取當前線程

2.4 線程的生命周期

  • 新建(new)
  • 就緒(runnable)
  • 運行(running)
  • 阻塞(blocked)
  • 死亡(dead)
Thread生命周期

==搶占式調(diào)度策略==

系統(tǒng)會給每個可執(zhí)行的線程一小段的時間來處理任務(wù);當該時間段使用完,系統(tǒng)就會剝奪該線程所占據(jù)的資源,讓其他線程獲得執(zhí)行的機會。在選擇下一個線程時,系統(tǒng)會考慮線程的優(yōu)先級。
就緒和運行狀態(tài)之間的轉(zhuǎn)換通常不受程序控制,而是由系統(tǒng)線程調(diào)度所導致。

2.5 join

當在某個程序執(zhí)行流中A調(diào)用其他線程的join方法,A(調(diào)用join方法的那個線程)將被阻塞,知道join方法加入的join線程完成為止。

public class Join {
    
    public static class JoinThread implements Runnable{

        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
        }
        
    }
    
    public static void main(String[] args) {
        JoinThread jt = new JoinThread();
        new Thread(jt,"TXB").start();

        for(int i=0;i<100;i++){
            System.out.println("i:" + i);
            if(i == 20){
                Thread joinThread = new Thread(jt, "joinThread");
                joinThread.start();
                
                try {
                    joinThread.join();
                } catch (InterruptedException e) {
                    System.out.println("e:" + e);
                }
            }
        }
    }
}

上面程序一共有3條線程:

主線程開始之后啟動了名為“TXB”的線程,該子線程將會和main線程并發(fā)執(zhí)行

當主線程的循環(huán)變量i等于20時,啟動了名為“joinThread”的線程,然后這個線程join進了main線程。注意:此時“joinThread”不會和main線程并發(fā)執(zhí)行,而是main線程必須等該線程執(zhí)行結(jié)束后才可以向下執(zhí)行。在“joinThread”執(zhí)行時,實際上只有兩條子線程(“TXB” 和 “joinThread”)并發(fā)執(zhí)行,而main線程處于等待(阻塞)狀態(tài)知道“joinThread”執(zhí)行完。

執(zhí)行結(jié)果

2.6 后臺程序(Daemon Thread)

  • 指在后臺運行的 線程,任務(wù)是為其他的線程提供服務(wù)。 JVM的垃圾回收線程就是典型的后臺線程。

  • 特征:如果所有的前臺線程都死亡,那么后臺線程會自動死亡。當整個虛擬機中只剩下后臺線程時,程序就沒有繼續(xù)運行的必要了,所以虛擬機也就退出了。

  • 設(shè)置指定線程為后臺線程: 調(diào)用Thread對象的setDaemon()方法

  • 前臺線程創(chuàng)建的子線程默認是前臺線程,后臺線程創(chuàng)建的子線程默認是后臺線程

public class DaemonThread {
    
    public static class Daemon implements Runnable{

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
        }
        
    }
    
    public static void main(String[] args) {
        
        Daemon d = new Daemon();
        Thread t = new Thread(d, "DaemonThread");

        t.setDaemon(true);

        t.start();

        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
        }

    }
}
執(zhí)行結(jié)果

上面代碼在main方法里先將t設(shè)置為后臺線程,然后啟動該線程(==要將某個線程設(shè)置為后臺線程,必須在該線程啟動之前設(shè)置,setDaemon(true)必須在start()之前調(diào)用,否則會引發(fā)IllegalThreadStateException==)。本來該線程和ing該執(zhí)行到i = 99才會結(jié)束,但是實際上它無法運行到99,因為主線程(程序中唯一的前臺線程)運行結(jié)束后,JVM會自動退出,后臺線程也就自動死亡了。

2.7 sleep和yield

sleep方法:Thread類的靜態(tài)方法,讓當前正在執(zhí)行的線程暫停一段時間,并且進入阻塞狀態(tài)。當當前線程調(diào)用sleep方法進入阻塞狀態(tài)之后,在它sleep的時間里,它不會獲得執(zhí)行的機會。就算系統(tǒng)中沒有其他可運行的程序,處于sleep的線程也不會運行,因此sleep方法常用于暫停程序的運行。

static void sleep(long millis)
 
static void sleep(long millis,int nanos)

yield:和sleep有點類似,也是Thread類的一個靜態(tài)方法。它也可以讓當前正在執(zhí)行的線程暫停,但不會使線程阻塞,只是將線程轉(zhuǎn)入就緒狀態(tài)。

sleep yield
sleep暫停當前線程之后,會給其他線程執(zhí)行機會,并不考慮線程優(yōu)先級 yield方法暫停當前線程之后,==只有和當前線程優(yōu)先級相同或者更高的處于就緒狀態(tài)(runnable)的線程才能有執(zhí)行的機會==
sleep方法會將線程轉(zhuǎn)入阻塞狀態(tài),知道經(jīng)過了設(shè)定的阻塞時間才會轉(zhuǎn)到就緒狀態(tài) yield方法不會將線程轉(zhuǎn)入阻塞狀態(tài),它只是強制讓當前線程從運行狀態(tài)(runnig)轉(zhuǎn)到就緒狀態(tài)(runnable)。因此完全有可能某個線程調(diào)用yield暫停之后又馬上獲得CPU資源被執(zhí)行
sleep方法會拋出InterruptedException異常 不拋異常
sleep方法比yiled方法具有更好的移植性 通常不要依靠yield來控制并發(fā)線程的執(zhí)行

2.9生產(chǎn)者和消費者

死鎖出現(xiàn)的原因

產(chǎn)生死鎖必須同時滿足以下四個條件,只要其中任一條件不成立,死鎖就不會發(fā)生.

  • 1.互斥條件:線程要求對所分配的資源進行排他性控制,即在一段時間內(nèi)某 資源僅為一個進程所占有.此時若有其他進程請求該資源.則請求進程只能等待.
  • 2.不剝奪條件:進程所獲得的資源在未使用完畢之前,不能被其他進程強行奪走,即只能由獲得該資源的線程自己來釋放(只能是主動釋放).
  • 3.請求和保持條件:線程已經(jīng)保持了至少一個資源,但又提出了新的資源請求,而該資源已被其他線程占有,此時請求線程被阻塞,但對自己已獲得的資源保持不放.
  • 4.循環(huán)等待條件:存在一種線程資源的循環(huán)等待鏈,鏈中每一個線程已獲得的資源同時被鏈中下一個線程所請求。
 // 產(chǎn)品
    static class ProductObject {
        // 線程操作變量可見
        public volatile static String value;
    }

    // 生產(chǎn)者線程
    static class Producer extends Thread {
        Object lock;

        public Producer(Object lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            // 不斷生產(chǎn)產(chǎn)品
            while (true) {
                synchronized (lock) { // 互斥鎖
                    // 產(chǎn)品還沒有被消費,等待
                    if (ProductObject.value != null) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    // 產(chǎn)品已經(jīng)消費完成,生產(chǎn)新的產(chǎn)品
                    ProductObject.value = "NO:" + System.currentTimeMillis();
                    System.out.println("生產(chǎn)產(chǎn)品:" + ProductObject.value);
                    lock.notify(); // 生產(chǎn)完成,通知消費者消費
                } 
            }
        }
    }

    // 消費者線程
    static class Consumer extends Thread {
        Object lock;

        public Consumer(Object lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    // 沒有產(chǎn)品可以消費
                    if (ProductObject.value == null) {
                        // 等待,阻塞
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("消費產(chǎn)品:" + ProductObject.value);
                    ProductObject.value = null;
                    lock.notify(); // 消費完成,通知生產(chǎn)者,繼續(xù)生產(chǎn)
                }
            
            }
        }
    }
    
    

3. Callabe , Future ,線程池

Callable是Runnable接口的增強版,Callable也提供了一個call()方法作為線程執(zhí)行體

call()方法可以有返回值

call()方法可以聲明拋出異常

Callable問題

Callable接口并不是Runnable接口的子接口,而Thread的構(gòu)造方法里形參的類型Runnable,所以Callable對象不能直接做為Thread的target;而且call方法不能直接調(diào)用,而是作為線程執(zhí)行體被調(diào)用。

為了解決這幾個問題:

java提供了Future接口來代替Callable接口的call方法,并為Futrue接口提供一個FutureTast實現(xiàn)類,這個實現(xiàn)了Future接口,也實現(xiàn)了Runnable接口。

線程池

線程池在系統(tǒng)啟動時就創(chuàng)建了大量空閑的線程,程序?qū)⒁粋€Runnable對象傳給線程池,線程池就會啟動一條線程來執(zhí)行該對象的run方法,當run方法執(zhí)行結(jié)束之后,該線程不會死亡,而是再次返回線程池中成為空閑狀態(tài),等待執(zhí)行下一個Runnable對象的run方法。

Executors工廠類來生產(chǎn)線程池

方法 描述
newCachedThreadPool() 創(chuàng)建一個具有緩存功能的線程池,系統(tǒng)根據(jù)需要創(chuàng)建線程,這線程會被緩存在線程池中
newFixedThreadPool(int nThreads) 創(chuàng)建一個可重用的、具有固定線程數(shù)的線程池
newSingleThreadExecutor() 創(chuàng)建一個只有單線程的線程池,相當于newFixedThreadPool(int nThreads)傳入?yún)?shù)為1
newScheduledThreadPool(int corePoolSize) 創(chuàng)建具有固定線程數(shù)的線程池,可以在指定延遲后執(zhí)行線程任務(wù)。corePoolSize指池中所保存的線程數(shù),即使線程是空閑的也被保存在線程池里
newSingleThreadScheduledExecutor() 創(chuàng)建只有固定線程的線程池,可以在指定延遲后執(zhí)行線程任務(wù)。

前三個方法返回一個ExecutorService對象,該對象代表一個線程池,可以執(zhí)行Runnable或Callable對象所代表的線程。

后兩個方法返回一個ScheduledExecutorService,是ExecutorService的子類,可以在指定延遲后執(zhí)行線程任務(wù)。

使用線程池來執(zhí)行線程任務(wù)的步驟:

  • 調(diào)用Executors類的靜態(tài)工廠方法創(chuàng)建一個ExecutorService對象,該對象代表一個線程池。
  • 創(chuàng)建Runnable或Callable實現(xiàn)類的實例,作為線程任務(wù)
  • 調(diào)用ExecutorService對象的submit方法來提交Runnable或Callable任務(wù)
  • 當不想再提交任何任務(wù)時調(diào)用ExecutorService對象的shutdown方法來關(guān)閉線程池
public static void main(String[] args) {
        Task work = new Task();
        FutureTask<Integer> future = new FutureTask<Integer>(work){
            //異步任務(wù)執(zhí)行完成,回調(diào)
            @Override
            protected void done() {
                try {
                    System.out.println("done:"+get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        };
        //線程池(使用了預(yù)定義的配置)
        ExecutorService executor = Executors.newCachedThreadPool();
        //executor.submit(future);
        executor.execute(future);
//      new Thread(future,"TXB").start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        //取消異步任務(wù)
        //future.cancel(true);
        
        try {
            //阻塞,等待異步任務(wù)執(zhí)行完畢
            System.out.println(future.get()); //獲取異步任務(wù)的返回值
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
    
    //異步任務(wù)
    static class Task implements Callable<Integer>{

        //返回異步任務(wù)的執(zhí)行結(jié)果
        @Override
        public Integer call() throws Exception {
            int i = 0;
            for (; i < 10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + "_"+i);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return i;
        }   
    }

4.android中的AsyncTask

在android中調(diào)用流程


AsyncTask流程

模擬android中的AsyncTask

public static void main(String[] args) {
        int CPU_COUNT = Runtime.getRuntime().availableProcessors();  //可用的CPU個數(shù)
        int CORE_POOL_SIZE = CPU_COUNT + 1; //5
        int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; //9
        int KEEP_ALIVE = 1;
        
        //任務(wù)隊列(128)
        final BlockingQueue<Runnable> sPoolWorkQueue =
                new LinkedBlockingQueue<Runnable>(128);
        
        //線程工廠
        ThreadFactory sThreadFactory = new ThreadFactory() {
            private final AtomicInteger mCount = new AtomicInteger(1);

            public Thread newThread(Runnable r) {
                String name = "Thread #" + mCount.getAndIncrement();
                System.out.println(name);
                return new Thread(r, name);
            }
        };
        
        //線程池
        Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
        
        //執(zhí)行異步任務(wù)
        //如果當前線程池中的數(shù)量大于corePoolSize,緩沖隊列workQueue已滿,
        //并且線程池中的數(shù)量等于maximumPoolSize,新提交任務(wù)由Handler處理。
        //RejectedExecutionException
        for (int i = 0; i < 200; i++) {
            //相當于new AsyncTask().execute();
            THREAD_POOL_EXECUTOR.execute(new MyTask());
        }
    }
    
    static class MyTask implements Runnable{

        @Override
        public void run() {
            //System.out.println(Thread.currentThread().getName());
            while(true){
                try {
                    System.out.println(Thread.currentThread().getName());
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        
    }
問題

會出現(xiàn)這問題:這個問題產(chǎn)生的原因是默認情況任務(wù)隊列只分配了128,如果我們生存了200個任務(wù),就會出現(xiàn)這個問題。還有一個關(guān)鍵是任務(wù)阻塞了,也就是很耗時的時候。

怎么解決了:我們可以進行線程池擴容
Executor THREAD_POOL_EXECUTOR = Executors.newFixedThreadPool(25);

在android中調(diào)用異步任務(wù)時使用new MyTask().executeOnExecutor(exec);

在這有可能出現(xiàn)內(nèi)存泄露的問題。

產(chǎn)生的原因是如果在子線程中一直處理一些事情,時間比較長,activity在旋轉(zhuǎn)或退出的時候,這個線程還好執(zhí)行,并保存activity的引用,讓其無法釋放。

解決方法:

  @Override
    protected void onDestroy() {
        super.onDestroy();
        task.cancel(true); // 取消任務(wù)
    }
    
  class MyTask extends AsyncTask<Void, Integer, Void> {
        int i = 0;

        @Override
        protected Void doInBackground(Void... params) {
            while (!isCancelled()) {
                // Log.d("jason", String.valueOf(i++));
                SystemClock.sleep(1000);
            }
            return null;
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 寫在前面的話: 這篇博客是我從這里“轉(zhuǎn)載”的,為什么轉(zhuǎn)載兩個字加“”呢?因為這絕不是簡單的復制粘貼,我花了五六個小...
    SmartSean閱讀 4,946評論 12 45
  • 本文主要講了java中多線程的使用方法、線程同步、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法、概述等。 首先講...
    李欣陽閱讀 2,597評論 1 15
  • Java 多線程 線程和進程的區(qū)別 線程和進程的本質(zhì):由CPU進行調(diào)度的并發(fā)式執(zhí)行任務(wù),多個任務(wù)被快速輪換執(zhí)行,使...
    安安zoe閱讀 2,265評論 1 18
  • 該文章轉(zhuǎn)自:http://blog.csdn.net/evankaka/article/details/44153...
    加來依藍閱讀 7,469評論 3 87
  • 隨著年齡的增長 對bling bling的東西不再有好感 反而喜歡簡單的線條 卡通的圖案 這便是所謂的裝嫩神器吧 ...
    PWong閱讀 184評論 0 0

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