Java线程池

一. ExecutorService的创建方式

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

所有线程池最终都是通过这个方法来创建的。

corePoolSize : 核心线程数,一旦创建将不会再释放。如果创建的线程数还没有达到指定的核心线程数量,将会继续创建新的核心线程,直到达到最大核心线程数后,核心线程数将不在增加;如果没有空闲的核心线程,同时又未达到最大线程数,则将继续创建非核心线程;如果核心线程数等于最大线程数,则当核心线程都处于激活状态时,任务将被挂起,等待空闲线程来执行。

maximumPoolSize : 最大线程数,允许创建的最大线程数量。如果最大线程数等于核心线程数,则无法创建非核心线程;如果非核心线程处于空闲时,超过设置的空闲时间,则将被回收,释放占用的资源。

keepAliveTime : 也就是当线程空闲时,所允许保存的最大时间,超过这个时间,线程将被释放销毁,但只针对于非核心线程。

unit : 时间单位,TimeUnit.SECONDS等。

workQueue : 任务队列,存储暂时无法执行的任务,等待空闲线程来执行任务。

threadFactory : 线程工程,用于创建线程。

handler : 当线程边界和队列容量已经达到最大时,用于处理阻塞时的程序

常用的颗粒度

TimeUnit.DAYS					//天
TimeUnit.HOURS					//小时
TimeUnit.MINUTES				//分钟
TimeUnit.SECONDS				//秒
TimeUnit.MILLISECONDS			//毫秒 

1.可缓存线程池

ExecutorService cachePool = Executors.newCachedThreadPool();

看看它的具体创建方式:

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

通过它的创建方式可以知道,创建的都是非核心线程,而且最大线程数为Interge的最大值,空闲线程存活时间是1分钟。如果有大量耗时的任务,则不适该创建方式。它只适用于生命周期短的任务。

2.单线程池

ExecutorService singlePool = Executors.newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

只用一个线程来执行任务,保证任务按FIFO顺序一个个执行。

3.固定线程数线程池

Executors.newFixedThreadPool(3);
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

也就是创建固定数量的可复用的线程数,来执行任务。当线程数达到最大核心线程数,则加入队列等待有空闲线程时再执行。

4.固定线程数,支持定时和周期性任务

ExecutorService scheduledPool = Executors.newScheduledThreadPool(5);
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue());
}

可用于替代handler.postDelay和Timer定时器等延时和周期性任务。

public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                              long initialDelay,
                                              long period,
                                              TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                 long initialDelay,
                                                 long delay,
                                                 TimeUnit unit);

scheduleAtFixedRate和sheduleWithFixedDelay有什么不同呢?

scheduleAtFixedRate:创建并执行一个在给定初始延迟后的定期操作,也就是将在 initialDelay 后开始执行,然后在initialDelay+period 后下一个任务执行,接着在 initialDelay + 2 * period 后执行,依此类推 ,也就是只在第一次任务执行时有延时。

sheduleWithFixedDelay:创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟,即总时间是(initialDelay + period)*n


二.ThreadPoolExecutor

private ExecutorService pool = new ThreadPoolExecutor(3, 10,
            10L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(512), Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

可以根据自己的需求创建指定的核心线程数和总线程数。

BlockingQueue 詳細介紹

基本規則為

  1. 如果當前的 Thread 小於 corePoolSize,則 Executor 首先會新增 Thread,而不會把 Task 丟到Queue 之中 (基本上就是直接運行的意思)
  2. 如果當前的 Thread 大於等於 corePoolSize,則 Executor 首先會把 Task 加到 Queue 之中等待
  3. 當 Task 無法再被加入到 Queue 之中的話,則 Executor 首先會創建新的 Thread,直到超過 maxPoolSize為止
  4. 超過 maxPoolSize 時,任務會被拒絕
BlockingQueue 有三種類型

直接提交
代表類型: synchronousQueue
基本上就 Queue Size 就是 0
會直接把 Task 提交給 Thread,如果不存在可用 Thread,則新建一個
如果此類型有設置 maxPoolSize 的話,是有可會拒絕新的 Task
所以通常使這種類型,會建議 maxPoolSize 不要做上限設定

無界隊列 (Unbounded Queue)
代表類型: LinkedBlockingQueue
Queue 的大小是無限制的
特別注意的是因為大小是無限制,所以萬一 Task 執行時間過長
會導致有大量個 Task 卡在 Queue 之中動彈不得,進而導致 OOM 的發生
Executors.newFixedThreadPool 採用的就是此種類型的 Queue

有界隊列 (Bounded Queue)
代表類型: ArrayBlockingQueue
Queue 的大小是有限制的
但要注意的點是,這個 Queue 大小必須和 Thread Pool 相互搭配才可以發揮出比較好的效能
使用大的 Queue Size 和小的 Thread Pool Size
雖然可以有效降低 CPU 使用率,但會降低 QPS
而使用小的 Queue Size 和大的 Thread Pool Size
雖然可以提昇 QPS,但會降低 CPU

Queue 飽和 RejectExecutionHandle 介紹

再來要介紹當 Queue 飽和之後,可以根據不同 handle 做出不一樣的行為
以下總計有四種使用方式

終止策略 (AbortPolicy)
此為預設 Policy
使用該 Policy,飽和時會拋出 RejectedExecutionException
調用者可以用以下自行定義方式處理異常

executor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println("Get you!");
        r.run();
        System.out.println("Done in handler");
    }
});

拋棄策略 (DiscardPolicy)
不做任何處理直接拋棄

拋棄舊任務策略 (DiscardOldestPolicy)
把 Queue 之中最頭的元素拋棄,並在嘗試重新提交 Task

調用者運行策略 (CallerRunsPolicy)
簡單來說,飽和後會直接由調用 Thread Pool 的主 Thread 自己來執行這個 Task
但在這個期間,主 Thread 就無法再度提交 Task
從而讓 Thread Pool 有時間把正在處理的 Task 給完成

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值