java多線程以及Android多線程

Java 多線程

線程和進程的區別

  • 線程和進程的本質:由CPU進行調度的併發式執行任務,多個任務被快速輪換執行,使得宏觀上具備多個線程或者進程同時執行的效果。html

  • 進程:在操做系統來講,一個運行的程序或者說一個動態的指令集合一般對應一個進程Process,它是系統進行資源分配和調度的一個獨立單位,也是擁有系統資源的基本單位。進程是系統中獨立存在的實體,它能夠擁有本身獨立的資源,擁有本身私有的地址空間,進程之間不能直接訪問其餘進程的地址空間。
  • 線程:線程是CPU調度的基本單位,也就是說在一個進程中能夠有多個併發程序執行流,線程拓展了進程的概念,使得任務的執行獲得更加的細分,因此Thread有時候也被稱爲Lightweight Process。線程是進程的執行單元,可是線程不是分配系統資源的單位,它們共享所在進程的資源,包括共享內存,公有數據,全局變量,進程文件描述符,進程處理器,進程代碼段,進程用戶ID等等。
  • 線程獨立擁有本身的線程ID,堆棧,程序計數器,局部變量,寄存器組值,優先級,信號屏蔽碼,錯誤返回碼等等,線程是獨立運行的,其執行是搶佔式的。線程共享進程資源,線程之間的通訊要進程之間的通訊來得容易得多。此外,線程的建立和銷燬的開銷也遠遠小於進程的系統開銷。java

線程和線程池

  • 線程池:雖然線程的建立銷燬的開銷相對較小,可是頻繁得建立和銷燬也會消耗有限的資源,從而帶來性能上的浪費,也不夠高效。所以線程池的出,現就是爲了解決這一問題,即在初始狀態建立並維護必定數量的空閒線程,當有須要執行的任務,就交付給線程中的一個線程,任務執行結束後,該線程也不會死亡,而是回到線程池中從新變爲空閒狀態。
  • 線程池的好處:減小線程頻繁建立銷燬的資源開銷,同時可以有效控制系統中併發線程的數量,防止系統性能的劇烈降低。

線程建立/啓動的三種方法

  • 繼承Thread類建立多線程,此時每次建立的Thread對象並不能共享線程類的實例變量,也就是下面程序中的i
public class FirstThread extends Thread{
    private int i;
    @Override
    public void run() {
        for(i=0;i<10;i++) 
            System.out.println(getName()); // 繼承自Thread 
    }
    
    public static void main(String[] args) {    
        new FirstThread().start();
        new FirstThread().start(); // 注意啓動線程須要用Start
    }
}
  • 實現Runnable接口建立線程類,Runnable接口是一個函數式接口(可使用Lambda表達式),一般作法是重寫接口中的run方法,此時方法體即爲線程執行體,使用Runnable接口實現類的實例做爲Thread的target來建立Thread對象,此時由於使用一個共同的target線程執行體,多個線程能夠共享一個實例變量。
public class SecondThread implements Runnable{
    private int i;
    @Override
    public void run() {
        for(;i<10;i++) {
            System.out.println(Thread.currentThread().getName() + " "+ i);
        }
    }
    
    public static void main(String[] args) {
        SecondThread targetRunnable = new SecondThread();
        new Thread(targetRunnable,"線程1").start();
        new Thread(targetRunnable).start();
    }
}
  • 使用Callable和Future建立線程:Callable相似於Runnable,提供一個Call()方法做爲線程執行體,而且能夠有返回值,以及拋出異常,那麼咱們如何拿到返回值,java提供了future接口,在接口裏定義了一些公共方法來控制關聯它的Callable任務,而後java還貼心的給了FutureTask類,該類實現了Future接口和Runnable接口,因此FutureTask的實例就能夠做爲Thread的Target,因此一般作法是建立Callable接口實現類,並對該實現類的實例使用FutureTask來包裝。
public class ThridThread {
    public static void main(String[] args) {        
        // lambda 表達式 + functionInterface 類型轉換
        // Callbable: 有返回值
        FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{
            int i =0;
            for(;i<100;i++) {
                System.out.println(Thread.currentThread().getName() + " "+ i);
            }
            return i;
    }); 
        new Thread(task,"有返回值的線程").start();
        try {
            System.out.println("子線程的返回值"+task.get());
        }catch(Exception e) {
            e.printStackTrace();
        }
 }
}

線程的生命週期

  • 線程的生命週期包括:新建New,就緒Runnable,運行Running,阻塞Blocked,和死亡Dead 5種狀態。
  • 新建和就緒狀態:程序使用new關鍵字以後,該線程就處於新建狀態,jvm爲其分配內存,並初始化成員變量。程序調用start() 方法以後,該線程就處於就緒狀態,jvm爲其建立方法調用棧和PC計數器。
  • 運行和阻塞狀態:若是就緒狀態的線程得到了CPU,那麼程序就處於運行狀態。
    當發生以下狀況時,線程將進入阻塞狀態。
    1. 線程調用sleep()方法,主動放棄所佔有的CPU資源。
    2. 線程調用了一個阻塞式IO方法,在該方法返回以前,該線程被阻塞
    3. 線程試圖得到一個同步監視器,可是該同步監視器被其餘線程所持有
    4. 線程等待某個通知notify,notify一般與wait配合使用
    5. 線程調用suspend(),掛起,該方法容易形成死鎖,不建議使用。
    當發生以下狀況時,線程進入就緒狀態,可是線程何時進入運行狀態,須要根據系統調度來決定。
    1. sleep()方法的線程通過了指定的sleep的時間
    2. 阻塞式IO方法返回值
    3. 成功得到了同步監視器
    4. 線程得到了其餘線程發出的通知,被喚醒
    5. 掛起的線程調用了Resume()方法恢復。

屏幕快照 2017-12-01 上午11.58.30.png

屏幕快照 2017-12-01 下午2.19.31.png

  • 線程死亡:線程執行體執行結束,以及拋出一個未捕獲的Exception或Error,或者直接調用stop()方法結束該線程。能夠經過線程對象的isAlive()方法,來判斷線程對象的狀態(新建或者死亡都會返回false)

注意:搶佔式策略系統:系統會給每一個執行的線程一個小的時間段來處理任務,當該時間段用完以後,系統會剝奪該線程所佔用的資源,讓其餘線程得到執行的機會。在系統調度時,還會考慮到線程的優先級問題。android

線程控制

  • join()-線程:讓一個線程等待另外一個線程,當在某個線程執行流中調用其餘線程的join()方法,該線程將被阻塞,知道join線程執行完畢爲止。緩存

  • 後臺-線程:後臺線程又稱爲Daemon Thread,守護線程,JVM的垃圾回收線程就是典型的後臺線程。特徵是:若是全部前臺線程都死亡,那麼後臺線程自動死亡。調用Thread對象的setDaemon(true)能夠將指定線程設置爲後臺線程,注意須要在Start()以前調用,主線程默認爲前臺線程,前臺線程建立的子線程默認爲前臺線程,後臺線程建立的子線程默認爲後臺線程
  • sleep()-線程:sleep(ms)是Thread類的靜態方法,讓當前線程暫停millis毫秒,並進入阻塞狀態,睡眠狀態的線程不會釋放同步監視器,在此期間該線程不會得到執行的機會。注意使用sleep方法時須要捕捉InterruptedException或者拋出該異常
public class SleepThread {
    public static void main(String[] args) throws Exception{ // 注意異常
        for(int i =0;i<5;i++) {
            System.out.println("當前時間"+new Date());
            Thread.sleep(1000);
        }       
    }
}
  • yield():線程讓步,也是Thread的靜態方法,使得正在執行的線程暫停,但不會阻塞線程,只是交出CPU的控制權,將線程轉爲就緒狀態,讓系統調度器從新調度一次。當某個線程調用yield方法暫停後,只有優先級與當前線程相同,或者優先級比當前線程更高的線程纔有可能得到執行機會。安全

  • 改變線程優先級:setPriority(int newPriority),高優先級的線程能得到更多的執行機會。網絡

線程同步

  • 線程的同步的意義在於線程安全,也就是說有多個線程併發訪問同一個對象,而線程調度的不肯定性可能帶來潛在的安全問題。多線程

  • 同步監視器:java多線程引入同步監視器來解決同步問題,任什麼時候刻只能有一個線程得到對同步監視器的鎖定,當同步代碼塊執行完成後,該線程會釋放對同步監視器的鎖定。java容許任何對象做爲同步監視器,一般咱們使用可能被併發訪問的共享資源做爲同步監視器。併發

  • 同步代碼塊:顯式指定同步監視器
public class DrawThread extends Thread {
    private Account account;
    private double drawaccout;
    
    public DrawThread(String name,Account account,double drawaccount) {
        super(name);
        this.account = account;
        this.drawaccout= drawaccount;
    }
    
    public void run() {
        synchronized(account) {
            if(account.getBlance()>=drawaccount) {
                System.out.println(getName()+"取錢成功");
                try {
                    Thread.sleep(1);
                }catch(InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 同步方法:隱式指定同步監視器,使用synchronized關鍵字來修飾某個方法,此時該方法(非static方法)無需顯式指定同步監視器,同步監視器默認爲this,也就是調用該方法的對象
public synchronized void draw(double amount) {
        ……
    }
  • 同步監視器的釋放:線程會在如下幾種狀況下釋放同步監視器的鎖定。
    1. 當前線程的同步方法,同步代碼塊執行結束或者在執行中遇到break,return等終止了代碼塊的執行
    2. 同步代碼塊或者方法中出現未處理的Error或者Exception,致使異常結束
    3. **當前線程執行同步代碼塊或者同步方法時,程序中執行了同步監視器的wait()方法,wait是object的方法,範圍是該object實例所在的線程
  • 同步鎖:lock,更增強大的線程同步機制,經過顯式定義鎖對象來實現同步,也就是Lock對象,線程在訪問共享資源以前,須要先得到鎖對象。線程安全控制中比較經常使用的是ReetrantLock可重入鎖。一個線程能夠對已經加鎖的ReetrantLock再度加鎖。
class X{
        private final ReentrantLock lock = new ReentrantLock();
        
        //須要定義線程安全的方法
        public void foo() {
            lock.lock();//加鎖
            try {
                // 須要保證線程安全的代碼
            }
            finally {
                lock.unlock();//使用finally塊保證釋放鎖
            }
        }
    }
  • 死鎖的問題:
    產生死鎖的四個必要條件:
    (1) 互斥條件:一個資源每次只能被一個進程使用。
    (2) 請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。
    (3) 不剝奪條件:進程已得到的資源,在末使用完以前,不能強行剝奪。
    (4) 循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係。app

    在系統中已經出現死鎖後,應該及時檢測到死鎖的發生,並採起適當的措施來解除死鎖。目前處理死鎖的方法可歸結爲如下四種:
    1) 預防死鎖:這是一種較簡單和直觀的事先預防的方法。方法是經過設置某些限制條件,去破壞產生死鎖的四個必要條件中的一個或者幾個,來預防發生死鎖。預防死鎖是一種較易實現的方法,已被普遍使用。可是因爲所施加的限制條件每每太嚴格,可能會致使系統資源利用率和系統吞吐量下降。jvm

    2) 避免死鎖:該方法一樣是屬於事先預防的策略,但它並不須事先採起各類限制措施去破壞產生死鎖的的四個必要條件,而是在資源的動態分配過程當中,用某種方法去防止系統進入不安全狀態,從而避免發生死鎖。

    3) 檢測死鎖:這種方法並不須事先採起任何限制性措施,也沒必要檢查系統是否已經進入不安全區,此方法容許系統在運行過程當中發生死鎖。但可經過系統所設置的檢測機構,及時地檢測出死鎖的發生,並精確地肯定與死鎖有關的進程和資源,而後採起適當措施,從系統中將已發生的死鎖清除掉。

    4) 解除死鎖:這是與檢測死鎖相配套的一種措施。當檢測到系統中已發生死鎖時,須將進程從死鎖狀態中解脫出來。經常使用的實施方法是撤銷或掛起一些進程,以便回收一些資源,再將這些資源分配給已處於阻塞狀態的進程,使之轉爲就緒狀態,以繼續運行。死鎖的檢測和解除措施,有可能使系統得到較好的資源利用率和吞吐量,但在實現上難度也最大。

    java中應該避免死鎖的出現。

線程安全是以犧牲程序運行效率爲代價的,所以在注意線程安全的同時,也要注意不要濫用鎖和同步方法,儘可能只對那些會改變競爭資源的方法進行同步。同時還要根據單線程和多線程運行環境來提供線程不安全和線程安全兩種版本,JDK提供的StringBuilder,StringBuffer就是一個例子。

線程通訊

  • Object類提供的wait(),notify(),notifyAll()三個方法,由同步監視器來調用,對於同步方法,其同步監視器是默認實例this,能夠再同步方法中直接調用這三個方法。
    1. wait(): 當前線程等待或者等待若干ms,當前線程自動釋放同步監視器,線程進入等待狀態(阻塞),直到其餘線程調用了該同步監視器的notify()或者notifyAll方法。
    2. notify():喚醒在同步監視器上等待的單個線程,如有多個線程等待,則任意選擇其中一個。
    3. notifyAll():喚醒在此同步監視器上等待的全部線程。
  • 使用Condition控制線程通訊:使用Lock對象來保證同步問題時,咱們可使用Condition類來釋放Lock以及喚醒其餘等待線程。
private final Lock lock = new ReentrantLock();
    // Condition實例綁定在一個Lock對象上
    private final Condition cond = lock.newCondition();
    
    public void Draw(double drawamount) {
        lock.lock();
        try {
            if(!flag)
                cond.await();//致使當前線程等待
            else {
                // ...
                cond.signalAll();// 喚醒其餘線程
            }
        }catch(InterruptedException e) {
            e.printStackTrace();
        }
        finally {
            lock.unlock();
        }
    }
  • 使用阻塞隊列(BlockingQueue)控制線程通訊:當生產者線程試圖向Blocking Queue中放入元素時,若是隊列已滿,則該線程被阻塞,當消費者線程試圖從BlockingQueue中取出元素時,若是隊列已空,則該線程阻塞。

線程池

  • 使用線程池執行線程任務的步驟是:
    1. 調用Executors類的靜態工廠方法建立一個ExecutorService對象,該對象表明一個線程池。
    2. 建立Runnable實現類或者Callable實現類的實例,做爲線程的執行任務。
    3. 調用ExecutorService對象的submit方法來提交Runnable或者Callable實例。
    4. 當沒有任務時,使用shutdown()方法來關閉線程池。
    public class Testjava{
      public static void main(String[] args)
      throws Exception{
          ExecutorService pool = Executors.newFixedThreadPool(6);
          Runnable target = ()->{
              for(int i=0;i<100;i++) {
              System.out.println(Thread.currentThread().getName()
                          + "的i值爲:"+ i);
              }
          };      
          // 向線程池中提交兩個線程
          pool.submit(target);
          pool.submit(target);        
          pool.shutdown();
      }
    }
  • Executors是一個工廠類,它包含了以下幾個靜態工廠方法來建立線程池。
    20150827155651746.png
    1. newCachedThreadPool():建立一個具備緩存功能的線程池,系統根據須要建立線程
    2. newFixedThreadPool(int nThreads):建立一個可重用的,具備固定線程數的線程池
    3. newSingleThreadExecutor():建立一個單線程線程池
    4. newScheduledThreadPool(int corePoolSize):建立具備指定線程數的線程池,它將在指定延遲後執行線程任務
    5. newSingleThreadScheduledExecutor():建立一個延遲執行的單線程線程池
    6. newWorkingStealingPool(int parallelism):建立持有足夠的線程的線程池來支持給定的並行級別,以充分支持多CPU並行能力。
    7. newWorkingStealingPool():根據CPU個數設置並行級別。
    12367 返回ExecutorService對象,表明一個線程池,能夠執行Runnable以及Callable對象所表明的線程,45返回的ScheduledExecutorService對象。

【Java8源碼分析】線程池-Executor與ExecutorService的全面剖析

線程相關類

  • ThreadLocal:Thread Local Variable線程局部變量,爲每一個使用該變量的線程提供一個變量值的副本,從而隔離多線程程序的競爭資源。ThreadLocal類的用法很簡單,它提供了三個public方法:
    1. T get():返回此線程局部變量當前線程副本的值
    2. void remove():刪除此線程局部變量中當前線程的值
    3. void set(T value):設置副本值
    class Accout{
          private ThreadLocal<String> name = new ThreadLocal<>();
          public Accout(String str) {
              this.name.set(str);
          }
          public String getname() {
              return name.get();
          }
          public void setname(String str) {
              this.name.set(str);
          }
      }

    注意:ThreadLocal與其餘同步機制都是爲了解決訪問同一資源衝突問題而出現的,可是側重的領域不一樣,同步機制爲實現多個線程對相同資源訪問的併發安全性,ThreadLocal則是隔離多個線程之間的數據共享,從而避免競爭。

  • 線程安全的集合類:
    1. ArrayList,LinkedList,HashSet,TreeSet,HashMap,TreeMap都是線程不安全的,若是須要在多線程中對上述集合類進行存取,須要使用Collections提供的靜態方法將其包裝成線程安全的類。
    2. 更好的方法是使用 java.util.concurrent包下提供的大量支持高效併發訪問的集合接口和實現類。如ConcurrentHashMap,ConcurrentLinkedQueue,ConcurrentLinkedDeque

Android多線程

Android中的多線程本質上也是Java的多線程,同時添加了一些不一樣的特性和使用的場景。其中,最主要的一個區別就是Android中主線程和子線程中的區分,Android中的主線程是UI線程,負責運行四大組件並與用戶實現交互,須要保持較高的反應速度,因此主線程不容許進行耗時的操做(好比說網絡請求和訪問),不然容易出現ANR現象,子線程則負責處理 一些耗時的任務,而若是子線程中想要實現對UI的操做,則須要經過Android的handler消息機制。

爲何子線程中不容許對UI進行操做呢
由於Android的UI控件並非線程安全,多線程的併發訪問會帶來UI控件的不可預期的狀態,且考慮到加鎖機制會帶來性能上的問題,所以Android在設計初期就禁止子線程處理UI。UI操做時ViewRootImpl會對操做者所在的線程進行checkThread,若是非主線程,會拋出CalledFromWrongThreadException。

那麼Android除了java原生的Thread/Runnable等線程形態,還有哪些包裝過了的有特色的線程形式?

  • AsyncTask: AsyncTask 封裝了線程池和Handler,主要是爲了方便開發者不去寫本身的後臺線程和定義Handler,而方便更新UI界面。
  • IntentService: IntentService從名字來看,能夠知道它是一個服務,其內部採用HandlerThread執行任務,執行完畢後自動退出。其特色是它是Service,比起其餘線程來講具備更高的優先級,不容易被系統殺死,而可以保證任務的執行。
  • HandlerThread: HandlerThread是一個具備消息循環loop的線程,也就是一開始就準備好了loop的線程,在其內部能夠直接使用Handler。

AsyncTask

很棒的參考:你真的瞭解AsyncTask

  • AsyncTask 是一個輕量級的一部任務類,在線程池中執行後臺任務。而後將任務的進度和最終結果傳遞給主線程,並在主線程中更新UI。

  • AsyncTask是一個抽象的泛型類,使用時必須繼承並實現它的子類,而且至少重寫doInBackground(Params...)方法。
// 三個泛型參數 不須要傳遞參數時,能夠用void代替
public abstract class AsyncTask<Params,Progress,Result>
  • AsyncTask的使用
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    //在線程池中執行 該方法必須返回計算結果給onPostExecute()
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            // 可使用該方法返回任務的進度,該方法會調用onProgressUpdate()
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }
    // 被主線程調用執行 所以這裏能夠有UI操做
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }
    // 在主線程中調用執行 任務執行結束後,會調用該方法,所以這裏能夠有UI操做
    protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
    }
}
// 主線程調用execute方法,執行任務前,會調用[1] onPreExecute()完成一些準備工做
// onPreExecute()是在主線程中執行
new DownloadFilesTask().execute(url1, url2, url3);
// 也能夠調用cancel來取消任務的執行
  • AsyncTask的使用注意事項:
    1. AsyncTask的類必須在主線程中加載,這一點液晶在Android4.1以上版本上自動完成
    2. AsyncTask 對象必須在主線程中建立
    3. execute方法必須在主線程中調用
    4. 不要直接調用 onPreExecute(),onPostExecute(Result)doInBackground(Params...), onProgressUpdate(Progress...)
    5. 一個AsyncTask對象只能執行一次,不然會運行報錯。
  • AsyncTask實現原理:
    1. AsyncTask中有兩個線程池:SerialExecutor用於任務的排隊,THREAD_POOL_EXECUTOR用於真正執行任務,定義了一個InternalHandler用於對UI進行操做,該handler是一個靜態的Handler對象,因此想要對UI進行操做,這個handler對象必須在UI線程中建立,也就是爲何AsyncTask類爲何要在UI線程加載。
    2. ThreadPoolExecutor是真正創建線程池的類,它在AsyncTask類中建立線程池的參數以下:
    public abstract class AsyncTask<Params, Progress, Result> {
      private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//CPU數
      private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
      private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
      private static final int KEEP_ALIVE = 1;
    
      private static final ThreadFactory sThreadFactory = new ThreadFactory() {
          private final AtomicInteger mCount = new AtomicInteger(1);
    
          public Thread newThread(Runnable r) {
              return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
          }
      };
    
      private static final BlockingQueue<Runnable> sPoolWorkQueue =
              new LinkedBlockingQueue<Runnable>(128);
    
      public static final Executor THREAD_POOL_EXECUTOR
              = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                      TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
    }
    // 核心線程數 = CPU數+1
    // 最大線程數 = CPU數*2 + 1
    // 非核心線程的超時時間爲1秒
    // 任務隊列的容量爲128

Android 開發手冊寫明:AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by thejava.util.concurrentpackage such as Executor,ThreadPoolExecutorand FutureTask

HandlerThread

  • HandlerThread 是Thread的子類,它的特色是可使用Handler的Thread。它的run方法體以下所示,外界能夠經過handler來通知HandlerThread來處理某些事情,從這能夠看出Handler的用處不只僅侷限於處理UI相關的問題。
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized(this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid =-1;   
}

IntentService

  • IntentService封裝了HandlerThread和Handler,可是它繼承了Service,因此致使它的優先級比單純線程要高,因此IntentService適合執行一些高優先級的後臺任務。

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue,
                      ThreadFactory threadFactory,
                      RejectedExecutionHandler handler) {
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
  • corePoolSize: 線程池的核心線程數,默認狀況下, 核心線程會在線程池中一直存活, 即便處於閒置狀態. 但若是將allowCoreThreadTimeOut設置爲true的話, 那麼核心線程也會有超時機制, 在keepAliveTime設置的時間事後, 核心線程也會被終止.
  • maximumPoolSize: 最大的線程數, 包括核心線程, 也包括非核心線程, 在線程數達到這個值後,新來的任務將會被阻塞.
  • keepAliveTime: 超時的時間, 閒置的非核心線程超過這個時長,講會被銷燬回收, 當allowCoreThreadTimeOut爲true時,這個值也做用於核心線程.
  • unit:超時時間的時間單位.
  • workQueue:線程池的任務隊列, 經過execute方法提交的runnable對象會存儲在這個隊列中.
  • threadFactory: 線程工廠, 爲線程池提供建立新線程的功能.
  • handler: 任務沒法執行時,回調handler的rejectedExecution方法來通知調用者.
  1. 若是線程池中線程的數目少於corePoolSize,就算線程池中有其餘的沒事作的核心線程,線程池仍是會從新建立一個核心線程;直到核心線程數目到達corePoolSize(常駐線程就位)

  2. 若是線程池中線程的數目大於或者等於corePoolSize,可是工做隊列workQueue沒有滿,那麼新的任務會放在隊列workQueue中,按照FIFO的原則依次等待執行;(當有核心線程處理完任務空閒出來後,會檢查這個工做隊列而後取出任務默默執行去)
  3. 若是線程池中線程數目大於等於corePoolSize,而且工做隊列workQueue滿了,可是總線程數目小於maximumPoolSize,那麼直接建立一個線程處理被添加的任務。
  4. 若是工做隊列滿了,而且線程池中線程的數目到達了最大數目maximumPoolSize,那麼就會用最後一個構造參數handler處理;**默認的處理方式是直接丟掉任務,而後拋出一個異常。

相關文章
相關標籤/搜索