java 四種執行緒池的使用

搜雲庫技術團隊發表於2017-10-12

介紹new Thread的弊端及Java四種執行緒池的使用

1,執行緒池的作用

執行緒池作用就是限制系統中執行執行緒的數量。

根據系統的環境情況,可以自動或手動設定執行緒數量,達到執行的最佳效果。
少了浪費了系統資源,多了造成系統擁擠效率不高。
用執行緒池控制執行緒數量,其他執行緒排 隊等候。 一個任務執行完畢,再從佇列的中取最前面的任務開始執行。
若佇列中沒有等待程式,執行緒池的這一資源處於等待。 當一個新任務需要執行時,如果執行緒池 中有等待的工作執行緒,就可以開始執行了;否則進入等待佇列。

2,為什麼要用執行緒池?

1.減少了建立和銷燬執行緒的次數,每個工作執行緒都可以被重複利用,可執行多個任務。

2.可以根據系統的承受能力,調整執行緒池中工作線執行緒的數目,防止因為消耗過多的記憶體,而把伺服器累趴下(每個執行緒需要大約1MB記憶體,執行緒開的越多,消耗的記憶體也就越大,最後當機)。

Java裡面執行緒池的頂級介面是Executor,但是嚴格意義上講Executor並不是一個執行緒池,而只是一個執行執行緒的工具。真正的執行緒池介面是ExecutorService

3,比較重要的幾個類

描述
ExecutorService 真正的執行緒池介面。
ScheduledExecutorService 能和Timer/TimerTask類似,解決那些需要任務重複執行的問題。
ThreadPoolExecutor ExecutorService的預設實現。
ScheduledThreadPoolExecutor 繼承ThreadPoolExecutor的ScheduledExecutorService介面實現,週期性任務排程的類實現。

4,new Thread的弊端

public class TestNewThread {

    public static void main(String[] args) {
        new Thread(new Runnable() {

            @Override
            public void run() {
                System.out.println("start");
            }
        }).start();
    }
}
複製程式碼

執行一個非同步任務你還只是如下new Thread嗎?

那你就out太多了,new Thread的弊端如下:

1.每次new Thread新建物件效能差。
2.執行緒缺乏統一管理,可能無限制新建執行緒,相互之間競爭,及可能佔用過多系統資源導致當機或oom。
3.缺乏更多功能,如定時執行、定期執行、執行緒中斷。

相比new Thread,Java提供的四種執行緒池的好處在於:

1.重用存在的執行緒,減少物件建立、消亡的開銷,效能佳。
2.可有效控制最大併發執行緒數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞。
3.提供定時執行、定期執行、單執行緒、併發數控制等功能。

四種執行緒池

Java通過Executors提供四種執行緒池,分別為:

1,newCachedThreadPoo

建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。

2,newFixedThreadPool

建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。

3,newScheduledThreadPool

建立一個定長執行緒池,支援定時及週期性任務執行。

4,newSingleThreadExecutor

建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。

示例

1,newCachedThreadPool

建立一個可快取的執行緒池。如果執行緒池的大小超過了處理任務所需要的執行緒, 那麼就會回收部分空閒(60秒不執行任務)的執行緒,當任務數增加時,此執行緒池又可以智慧的新增新執行緒來處理任務。此執行緒池不會對執行緒池大小做限制,執行緒池大小完全依賴於作業系統(或者說JVM)能夠建立的最大執行緒大小。

package io.ymq.thread.demo1;

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

/**
 * 描述: 建立一個可快取的執行緒池。如果執行緒池的大小超過了處理任務所需要的執行緒,那麼就會回收部分空閒(60秒不執行任務)的執行緒,當任務數增加時,此執行緒池又可以智慧的新增新執行緒來處理任務。
 * 此執行緒池不會對執行緒池大小做限制,執行緒池大小完全依賴於作業系統(或者說JVM)能夠建立的最大執行緒大小。
 *
 * @author yanpenglei
 * @create 2017-10-12 11:13
 **/
public class TestNewCachedThreadPool {
    public static void main(String[] args) {
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 1; i <= 10; i++) {
            final int index = i;
            try {
                Thread.sleep(index * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            cachedThreadPool.execute(new Runnable() {

                @Override
                public void run() {
                    String threadName = Thread.currentThread().getName();
                    System.out.println("執行:" + index + ",執行緒名稱:" + threadName);
                }
            });
        }
    }
}

複製程式碼

響應:

執行:1,執行緒名稱:pool-1-thread-1
執行:2,執行緒名稱:pool-1-thread-1
執行:3,執行緒名稱:pool-1-thread-1
執行:4,執行緒名稱:pool-1-thread-1
執行:5,執行緒名稱:pool-1-thread-1
執行:6,執行緒名稱:pool-1-thread-1
執行:7,執行緒名稱:pool-1-thread-1
執行:8,執行緒名稱:pool-1-thread-1
執行:9,執行緒名稱:pool-1-thread-1
執行:10,執行緒名稱:pool-1-thread-1
複製程式碼

2,newFixedThreadPool

描述:建立固定大小的執行緒池。每次提交一個任務就建立一個執行緒,直到執行緒達到執行緒池的最大大小。
執行緒池的大小一旦達到最大值就會保持不變,如果某個執行緒因為執行異常而結束,那麼執行緒池會補充一個新執行緒。

package io.ymq.thread.demo2;

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

/**
 * 描述:建立固定大小的執行緒池。每次提交一個任務就建立一個執行緒,直到執行緒達到執行緒池的最大大小。
 * 執行緒池的大小一旦達到最大值就會保持不變,如果某個執行緒因為執行異常而結束,那麼執行緒池會補充一個新執行緒。
 *
 * @author yanpenglei
 * @create 2017-10-12 11:30
 **/
public class TestNewFixedThreadPool {

    public static void main(String[] args) {

        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

        for (int i = 1; i <= 10; i++) {
            final int index = i;
            fixedThreadPool.execute(new Runnable() {

                @Override
                public void run() {
                    try {
                        String threadName = Thread.currentThread().getName();
                        System.out.println("執行:" + index + ",執行緒名稱:" + threadName);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {

                        e.printStackTrace();
                    }
                }
            });
        }

    }
}

複製程式碼

因為執行緒池大小為3,每個任務輸出index後sleep 2秒,所以每兩秒列印3個數字,和執行緒名稱。

響應:

執行:2,執行緒名稱:pool-1-thread-2
執行:3,執行緒名稱:pool-1-thread-3
執行:1,執行緒名稱:pool-1-thread-1

執行:4,執行緒名稱:pool-1-thread-1
執行:6,執行緒名稱:pool-1-thread-2
執行:5,執行緒名稱:pool-1-thread-3

執行:7,執行緒名稱:pool-1-thread-1
執行:9,執行緒名稱:pool-1-thread-3
執行:8,執行緒名稱:pool-1-thread-2

執行:10,執行緒名稱:pool-1-thread-1

複製程式碼

3,newScheduledThreadPool

建立一個定長執行緒池,支援定時及週期性任務執行。延遲執行

package io.ymq.thread.demo3;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 描述:建立一個定長執行緒池,支援定時及週期性任務執行。延遲執行
 *
 * @author yanpenglei
 * @create 2017-10-12 11:53
 **/
public class TestNewScheduledThreadPool {

    public static void main(String[] args) {

        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

        scheduledThreadPool.schedule(new Runnable() {

            @Override
            public void run() {
                System.out.println("表示延遲3秒執行。");
            }
        }, 3, TimeUnit.SECONDS);


        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                System.out.println("表示延遲1秒後每3秒執行一次。");
            }
        }, 1, 3, TimeUnit.SECONDS);
    }

}
複製程式碼
表示延遲1秒後每3秒執行一次。
表示延遲3秒執行。
表示延遲1秒後每3秒執行一次。
表示延遲1秒後每3秒執行一次。
表示延遲1秒後每3秒執行一次。
表示延遲1秒後每3秒執行一次。
複製程式碼

4,newSingleThreadExecutor

建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。

package io.ymq.thread.demo4;

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

/**
 * 描述:建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。
 *
 * @author yanpenglei
 * @create 2017-10-12 12:05
 **/
public class TestNewSingleThreadExecutor {

    public static void main(String[] args) {
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 1; i <= 10; i++) {
            final int index = i;
            singleThreadExecutor.execute(new Runnable() {

                @Override
                public void run() {
                    try {
                        String threadName = Thread.currentThread().getName();
                        System.out.println("執行:" + index + ",執行緒名稱:" + threadName);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}
複製程式碼

結果依次輸出,相當於順序執行各個任務。

響應:

執行:1,執行緒名稱:pool-1-thread-1
執行:2,執行緒名稱:pool-1-thread-1
執行:3,執行緒名稱:pool-1-thread-1
執行:4,執行緒名稱:pool-1-thread-1
執行:5,執行緒名稱:pool-1-thread-1
執行:6,執行緒名稱:pool-1-thread-1
執行:7,執行緒名稱:pool-1-thread-1
執行:8,執行緒名稱:pool-1-thread-1
執行:9,執行緒名稱:pool-1-thread-1
執行:10,執行緒名稱:pool-1-thread-1
複製程式碼

Contact

  • 作者:鵬磊
  • 出處:www.ymq.io
  • Email:admin@souyunku.com
  • 版權歸作者所有,轉載請註明出處
  • Wechat:關注公眾號,搜雲庫,專注於開發技術的研究與知識分享

關注公眾號-搜雲庫
搜雲庫

相關文章