首頁 > 軟體

Java十分鐘入門多執行緒下篇

2022-03-10 13:00:18

1、執行緒池:

什麼是執行緒池?

咱們也不看長篇大論,通俗的來講,執行緒池就是裝執行緒的容器,當需要用的時候去池裡面取出來,不用的時候放回去或者銷燬。這樣一個執行緒就可以反覆的利用,通過執行緒的這種反覆利用機制,可以有效地避免直接建立執行緒所帶來的壞處。

執行緒池有什麼好處?

  • 降低了資源的消耗(CPU)
  • 提高任務執行的響應速度
  • 提高執行緒的可管理性

執行緒池建立流程圖:

其實通過這個圖就可以看到執行緒池的處理過程:

  • 有新任務進來,判斷核心執行緒池是否滿了,是:進入排,否:建立任務
  • 在等待佇列判斷是否排滿了,是:進入執行緒池 ,否:任務加入佇列
  • 判斷執行緒池是否滿了,是:拒絕執行任務,否:建立執行緒執行

2、建立執行緒池:

先看一下官網給出的建立方法(部分):

完整的可以參考官方檔案:

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/Executors.html

這裡介紹四種常用的建立型別:

  • newCacheThreadPool(建立一個可快取的執行緒池,有任務時才會建立新的執行緒)
  • newSingleThreadExecutor(建立一個單執行緒池,執行緒池中只有一個執行緒)
  • newFixedThreadPool(int a) (建立固定執行緒數量的執行緒池,輸入引數:int型別)
  • newScheduledTreadPool (建立一個固定長度的執行緒池,並且以延時或定時的方式來執行執行緒)

1、newCacheThreadPool:

建立可快取的執行緒物件,意思是這個任務需要幾個執行緒來處理,就會建立幾個執行緒:

執行緒需要執行的類:

public class MyRunnable implements Runnable{

    int num;

    public MyRunnable(int num) {
        this.num = num;
    }

    @Override
    public void run() {
        
        System.out.println(Thread.currentThread().getName()+"執行了:"+num);
    }
}

測試類:

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

        //建立單個執行緒池物件,裡面執行緒只有一個
        ExecutorService cachedService = Executors.newCachedThreadPool();

        //執行5個任務
        for (int i = 1; i<= 5; i++) {
            cachedService.execute(new MyRunnable(i));
        }
    }
}

來看看結果:

OK,上述程式碼執行緒池用了5個執行緒來處理,那麼如果我們在每次執行前加一次執行緒休眠會怎麼樣? 在每次執行後需要休眠0.5秒(500毫秒):

public class Test {
    public static void main(String[] args) {
    
        //建立一個快取的執行緒池物件
       
        ExecutorService cachedService = Executors.newCachedThreadPool();
        
        for (int i = 1; i<= 5; i++) {
                cachedService.execute(new MyRunnable(i));
                //執行緒休眠:
                try{
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
    }
}

看看結果:

小結:

  • 如果沒有加執行緒休眠,執行緒池預設會建立多個執行緒池物件來幫你完成任務,執行更快完成並且銷燬記憶體,釋放資源。
  • 如果新增了執行緒休眠,執行緒池會認為同少量的執行緒物件就可以完成這個任務,就不會幫你建立多個執行緒物件(因為時間足夠,就沒必要再建立)

2、newSingleThreadExecutor:

這個方法是建立只有一個執行緒的執行緒池,不管怎麼樣,多隻有一個執行緒來幫你執行任務:

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

        //建立單個執行緒池物件,裡面執行緒只有一個
        ExecutorService singleService = Executors.newSingleThreadExecutor();

        //執行一百萬次任務
        for (int i = 1; i<= 1000000; i++) {
            singleService.execute(new MyRunnable(i));
        }
    }
}

看看結果:

對吧,執行了1000000次也是一個執行緒在執行,因為這個執行緒池裡面只有一個執行緒呀。

3、newFixedThreadPool(int a):

這個方法就是建立固定執行緒數的執行緒池,比如我要一個這個池裡面有10個執行緒,在後面輸入引數即可:

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

        //執行緒池建立固定數量的執行緒物件來執行任務,這裡建立10個執行緒物件,由執行緒池管理生命週期
        ExecutorService singleService = Executors.newFixedThreadPool(10);
        //執行10個任務
        for (int i = 1; i<= 100; i++) {
            singleService.execute(new MyRunnable(i));
        }
    }
}

看看結果:

4、newScheduledTreadPool :

建立一個固定長度的執行緒池,並且以延時或定時的方式來執行執行緒,也就是說使用這個方法建立的執行緒池可以自定義每次執行的時間:

Demo:

public class MyRunnable implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"延遲5秒執行,並且每2秒執行一次該任務:");
    }
}

測試類:

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

        //執行緒池建立一個定時任務的執行緒物件
        ScheduledExecutorService service = Executors.newScheduledThreadPool(4);
        /**
         *定時器執行的任務,並且按週期執行
         * 引數1:執行緒任務
         * 引數2:5秒之後開始執行
         * 引數3:執行後每2秒為一個週期去迴圈執行
         * 引數4:時間單位。列舉型別,我這指定秒(可以參考API)
         */
        service.scheduleAtFixedRate(new MyRunnable(),5,1, TimeUnit.SECONDS);
    }
}

看看結果:

3、執行緒池建立自定義執行緒:

當以上四種執行緒池滿不足業務需求的時候,咱們也可以自定義執行緒池:

先來一個執行緒執行類:

public class MyRunnable implements Runnable{

    int num;

    public MyRunnable(int i) {

        this.num = num;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"執行了"+num);
    }
}

測試類:

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

        /**
         * 引數1:執行緒池有5個固定的執行緒物件
         * 引數2:當執行緒池的執行緒忙不過來的時候,最大執行緒數為7,也就是說執行緒物件就可以增加到7個(其中2個是臨時執行緒)
         * 引數3:當臨時執行緒物件超出300毫秒的時候還沒有接到新任務,執行緒池就自動 銷燬臨時的執行緒物件
         * 引數4:時間單位。這指定秒,具體參考TimeUnit的列舉型別
         * 引數5:等待執行的任務序列4個(其中有4個任務序列等待執行緒池物件來執行任務)
         */

        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,7,300, 
        					TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(4));

        for (int i = 0; i < 6; i++) {
            executor.execute(new MyRunnable(i));
            showInfo(executor);
        }
    }

	//獲取執行緒的一些資訊
    private static void showInfo(ThreadPoolExecutor executor) {
        System.out.println("執行緒池的總數量:"+executor.getPoolSize());
        System.out.println("佇列中等待執行的任務數:"+executor.getQueue().size());
        System.out.println("已經執行完畢的任務數:"+executor.getCompletedTaskCount());
        System.out.println("---------");
    }
}

4、Runnable和Callable的區別:

Runnable是不返還值的,而Callable可以返回值,具體可以看這篇部落格(其實主要我也沒深入學習這個哈哈): Runnable和Callable的區別

5、執行緒池總結:

通過這個文章,咱們對執行緒池有了基礎的瞭解,如何去建立和使用,但這僅僅是最簡單的,執行緒是一個複雜的東西,大家也可以參考其他技術部落格來深入學習和研究,在建立執行緒池的時候,根據場景,合理設定執行緒池的各個引數,包括執行緒池數量、佇列、執行緒工廠和拒絕策略,讓資源更好的利用起來,這也是優化效能的關鍵。

到此這篇關於Java十分鐘入門多執行緒下篇的文章就介紹到這了,更多相關Java 多執行緒內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com