天天看點

Java知識【線程池】

1.線程池

1.1 線程狀态介紹

當線程被建立并啟動以後,它既不是一啟動就進入了執行狀态,也不是一直處于執行狀态。線程對象在不同的時期有不同的狀态。那麼Java中的線程存在哪幾種狀态呢?Java中的線程

狀态被定義在了java.lang.Thread.State枚舉類中,State枚舉類的源碼如下:

public class Thread {
    
    public enum State {
    
        /* 建立 */
        NEW , 

        /* 可運作狀态 */
        RUNNABLE , 

        /* 阻塞狀态 */
        BLOCKED , 

        /* 無限等待狀态 */
        WAITING , 

        /* 計時等待 */
        TIMED_WAITING , 

        /* 終止 */
        TERMINATED;
    
    }
    
    // 擷取目前線程的狀态
    public State getState() {
        return jdk.internal.misc.VM.toThreadState(threadStatus);
    }
    
}      

通過源碼我們可以看到Java中的線程存在6種狀态,每種線程狀态的含義如下

線程狀态 具體含義
NEW 一個尚未啟動的線程的狀态。也稱之為初始狀态、開始狀态。線程剛被建立,但是并未啟動。還沒調用start方法。MyThread t = new MyThread()隻有線程象,沒有線程特征。
RUNNABLE 當我們調用線程對象的start方法,那麼此時線程對象進入了RUNNABLE狀态。那麼此時才是真正的在JVM程序中建立了一個線程,線程一經啟動并不是立即得到執行,線程的運作與否要聽令與CPU的排程,那麼我們把這個中間狀态稱之為可執行狀态(RUNNABLE)也就是說它具備執行的資格,但是并沒有真正的執行起來而是在等待CPU的度。
BLOCKED 當一個線程試圖擷取一個對象鎖,而該對象鎖被其他的線程持有,則該線程進入Blocked狀态;當該線程持有鎖時,該線程将變成Runnable狀态。
WAITING 一個正在等待的線程的狀态。也稱之為等待狀态。造成線程等待的原因有兩種,分别是調用Object.wait()、join()方法。處于等待狀态的線程,正在等待其他線程去執行一個特定的操作。例如:因為wait()而等待的線程正在等待另一個線程去調用notify()或notifyAll();一個因為join()而等待的線程正在等待另一個線程結束。
TIMED_WAITING 一個在限定時間内等待的線程的狀态。也稱之為限時等待狀态。造成線程限時等待狀态的原因有三種,分别是:Thread.sleep(long),Object.wait(long)、join(long)。
TERMINATED 一個完全運作完成的線程的狀态。也稱之為終止狀态、結束狀态

各個狀态的轉換,如下圖所示:

Java知識【線程池】

1.2 線程池-基本原理

概述 :

提到池,大家應該能想到的就是水池。水池就是一個容器,在該容器中存儲了很多的水。那麼什麼是線程池呢?線程池也是可以看做成一個池子,在該池子中存儲很多個線程。

線程池存在的意義:

系統建立一個線程的成本是比較高的,因為它涉及到與作業系統互動,當程式中需要建立大量生存期很短暫的線程時,頻繁的建立和銷毀線程對系統的資源消耗有可能大于業務處理是對系

統資源的消耗,這樣就有點"舍本逐末"了。針對這一種情況,為了提高性能,我們就可以采用線程池。線程池在啟動的時,會建立大量空閑線程,當我們向線程池送出任務的時,線程池就

會啟動一個線程來執行該任務。等待任務執行完畢以後,線程并不會死亡,而是再次傳回到線程池中稱為空閑狀态。等待下一次任務的執行。

線程池的設計思路 :

  1. 準備一個任務容器
  2. 一次性啟動多個(2個)消費者線程
  3. 剛開始任務容器是空的,是以線程都在wait
  4. 直到一個外部線程向這個任務容器中扔了一個"任務",就會有一個消費者線程被喚醒
  5. 這個消費者線程取出"任務",并且執行這個任務,執行完畢後,繼續等待下一次任務的到來

1.3 線程池-Executors預設線程池

概述 : JDK對線程池也進行了相關的實作,在真實企業開發中我們也很少去自定義線程池,而是使用JDK中自帶的線程池。

我們可以使用Executors中所提供的靜态方法來建立線程池

​ static ExecutorService newCachedThreadPool() 建立一個預設的線程池

​ static newFixedThreadPool(int nThreads) 建立一個指定最多線程數量的線程池

代碼實作 :

package com.itheima.mythreadpool;


//static ExecutorService newCachedThreadPool()   建立一個預設的線程池
//static newFixedThreadPool(int nThreads)       建立一個指定最多線程數量的線程池

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

public class MyThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException {

        //1,建立一個預設的線程池對象.池子中預設是空的.預設最多可以容納int類型的最大值.
        ExecutorService executorService = Executors.newCachedThreadPool();
        //Executors --- 可以幫助我們建立線程池對象
        //ExecutorService --- 可以幫助我們控制線程池

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "在執行了");
        });

        //Thread.sleep(2000);

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "在執行了");
        });

        executorService.shutdown();
    }
}      

1.4 線程池-Executors建立指定上限的線程池

使用Executors中所提供的靜态方法來建立線程池

static ExecutorService newFixedThreadPool(int nThreads) : 建立一個指定最多線程數量的線程池

代碼實作 :

package com.itheima.mythreadpool;

//static ExecutorService newFixedThreadPool(int nThreads)
//建立一個指定最多線程數量的線程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class MyThreadPoolDemo2 {
    public static void main(String[] args) {
        //參數不是初始值而是最大值
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService;
        System.out.println(pool.getPoolSize());//0

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "在執行了");
        });

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "在執行了");
        });

        System.out.println(pool.getPoolSize());//2
//        executorService.shutdown();
    }
}      

1.5 線程池-ThreadPoolExecutor

建立線程池對象 :

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心線程數量,最大線程數量,空閑線程最大存活時間,任務隊列,建立線程工廠,任務的拒絕政策);

代碼實作 :

package com.itheima.mythreadpool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolDemo3 {
//    參數一:核心線程數量
//    參數二:最大線程數
//    參數三:空閑線程最大存活時間
//    參數四:時間機關
//    參數五:任務隊列
//    參數六:建立線程工廠
//    參數七:任務的拒絕政策
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,2,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());

        pool.shutdown();
    }
}      

1.6 線程池-參數詳解

Java知識【線程池】
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
    
corePoolSize:   核心線程的最大值,不能小于0
maximumPoolSize:最大線程數,不能小于等于0,maximumPoolSize >= corePoolSize
keepAliveTime:  空閑線程最大存活時間,不能小于0
unit:           時間機關
workQueue:      任務隊列,不能為null
threadFactory:  建立線程工廠,不能為null      
handler:        任務的拒絕政策,不能為null      

1.7 線程池-非預設任務拒絕政策

RejectedExecutionHandler是jdk提供的一個任務拒絕政策接口,它下面存在4個子類。

ThreadPoolExecutor.AbortPolicy:        丢棄任務并抛出RejectedExecutionException異常。是預設的政策。
ThreadPoolExecutor.DiscardPolicy:       丢棄任務,但是不抛出異常 這是不推薦的做法。
ThreadPoolExecutor.DiscardOldestPolicy:    抛棄隊列中等待最久的任務 然後把目前任務加入隊列中。
ThreadPoolExecutor.CallerRunsPolicy:        調用任務的run()方法繞過線程池直接執行。      

注:明确線程池對多可執行的任務數 = 隊列容量 + 最大線程數

案例示範1:示範ThreadPoolExecutor.AbortPolicy任務處理政策

public class ThreadPoolExecutorDemo01 {

    public static void main(String[] args) {

        /**
         * 核心線程數量為1 , 最大線程池數量為3, 任務容器的容量為1 ,空閑線程的最大存在時間為20s
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.AbortPolicy()) ;

        // 送出5個任務,而該線程池最多可以處理4個任務,當我們使用AbortPolicy這個任務處理政策的時候,就會抛出異常
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 執行了任務");
            });
        }
    }
}      

控制台輸出結果

pool-1-thread-1---->> 執行了任務
pool-1-thread-3---->> 執行了任務
pool-1-thread-2---->> 執行了任務
pool-1-thread-3---->>      

控制台報錯,僅僅執行了4個任務,有一個任務被丢棄了

案例示範2:示範ThreadPoolExecutor.DiscardPolicy任務處理政策

public class ThreadPoolExecutorDemo02 {
    public static void main(String[] args) {
        /**
         * 核心線程數量為1 , 最大線程池數量為3, 任務容器的容量為1 ,空閑線程的最大存在時間為20s
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.DiscardPolicy()) ;

        // 送出5個任務,而該線程池最多可以處理4個任務,當我們使用DiscardPolicy這個任務處理政策的時候,控制台不會報錯
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 執行了任務");
            });
        }
    }
}      

控制台輸出結果

pool-1-thread-1---->> 執行了任務
pool-1-thread-1---->> 執行了任務
pool-1-thread-3---->> 執行了任務
pool-1-thread-2---->>      

控制台沒有報錯,僅僅執行了4個任務,有一個任務被丢棄了

案例示範3:示範ThreadPoolExecutor.DiscardOldestPolicy任務處理政策

public class ThreadPoolExecutorDemo02 {
    public static void main(String[] args) {
        /**
         * 核心線程數量為1 , 最大線程池數量為3, 任務容器的容量為1 ,空閑線程的最大存在時間為20s
         */
        ThreadPoolExecutor threadPoolExecutor;
        threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.DiscardOldestPolicy());
        // 送出5個任務
        for(int x = 0 ; x < 5 ; x++) {
            // 定義一個變量,來指定指定目前執行的任務;這個變量需要被final修飾
            final int y = x ;
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 執行了任務" + y);
            });     
        }
    }
}      

控制台輸出結果

pool-1-thread-2---->> 執行了任務2
pool-1-thread-1---->> 執行了任務0
pool-1-thread-3---->> 執行了任務3
pool-1-thread-1---->> 執行了任務4      

由于任務1線上程池中等待時間最長,是以任務1被丢棄。

案例示範4:示範ThreadPoolExecutor.CallerRunsPolicy任務處理政策

public class ThreadPoolExecutorDemo04 {
    public static void main(String[] args) {

        /**
         * 核心線程數量為1 , 最大線程池數量為3, 任務容器的容量為1 ,空閑線程的最大存在時間為20s
         */
        ThreadPoolExecutor threadPoolExecutor;
        threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.CallerRunsPolicy());

        // 送出5個任務
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 執行了任務");
            });
        }
    }
}      
pool-1-thread-1---->> 執行了任務
pool-1-thread-3---->> 執行了任務
pool-1-thread-2---->> 執行了任務
pool-1-thread-1---->> 執行了任務
main---->>