线程池的三个方法,七个参数,四个拒绝策略

目录

线程池

三种方法创建线程池

看源码

ThreadPoolExecutor()的七个参数

AbortPolicy策略

CallerRunsPolicy策略

DiscardPolicy策略

DiscardOldestPolicy

最大线程数量怎么定?


线程池

三种方法创建线程池

java 复制代码
package com.qcby.pool;

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

/**
 * demo1
 * 了解了线程池的三大方法
 */
public class Demo1 {
    public static void main(String[] args) {
//        ExecutorService pool = Executors.newSingleThreadExecutor();// 单个线程的线程池
            ExecutorService pool = Executors.newFixedThreadPool(5); //创建固定大小的线程池
//        ExecutorService pool = Executors.newCachedThreadPool(); //可以伸缩的线程池
        try {
            for (int i = 1; i <= 10; i++) {
                final int temp = i;
                pool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "------>" + temp);
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            pool.shutdown();
        }
    }
}

单例的线程池

固定大小的线程池

可伸缩的线程池

看源码

java 复制代码
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                    0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>());
}

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

它们三个的本质都是调用了ThreadPoolExecutor

由它们的构造器当中可以看到,newSingleThreadExecutor和newFixedThreadPool(默认的队列最大长度),它们允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量请求,导致OOM;CachedThreadPool允许创建线程的数量是Integer.MAX_VALUE(因为SynchronousQueue没有容量,要插入必须等待别的线程对里面的请求进行移除,不会产生堆积),可能会创建大量的线程,导致OOM.

ThreadPoolExecutor()的七个参数

它的构造器有7个参数:

|-----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 参数名 | 含义 | 作用 |
| corePoolSize | 核心线程数量 | 当提交一个新任务时,如果当前运行的线程数少于 corePoolSize,即使有空闲线程,线程池也会创建一个新线程来处理该任务。 |
| maximumPoolSize | 最大线程数量 | 当任务队列(workQueue)满了之后,如果当前运行的线程数小于 maximumPoolSize,线程池会继续创建新的线程来处理任务(直到达到这个上限) |
| keepAliveTime | 线程空闲存活时间 | 如果线程池中的线程数超过了 corePoolSize,并且某个线程的空闲时间达到了 keepAliveTime,该线程会被回收销毁(直到线程数回到 corePoolSize) |
| unit | keepAliveTime参数的时间单位 | ---- |
| workQueue | 任务队列 | 用于存放等待执行的任务的阻塞队列,当线程池中的线程数已达到corePoolSize时,新提交的任务会先被放入这个队列中等待。 |
| threadFactory | 用于创建新线程的工厂 | 可以自定义它来设置线程的名称(方便调试)、优先级、是否为守护线程,或者统一记录线程创建日志。 默认值:如果不传,会使用 Executors.defaultThreadFactory(),生成的线程名称为 pool-{number}-thread-{number}。 |
| handler | 当任务无法被接受时触发的处理策略 | 当线程池已关闭,或线程数已达到 maximumPoolSize 且任务队列已满时。 内置策略 (均实现自 RejectedExecutionHandler): AbortPolicy (默认):直接抛出 RejectedExecutionException。 CallerRunsPolicy :由调用者(提交任务的线程)自己执行该任务。 DiscardPolicy :直接丢弃该任务,不抛异常。 DiscardOldestPolicy:丢弃队列中最旧的任务,然后重试提交当前任务。 |

java 复制代码
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

创建线程池:

java 复制代码
package com.qcby.pool;

import java.util.concurrent.*;

/**
 * 玩线程池
 * 模拟银行办理业务场景
 */
public class Demo2 {

    public static void main(String[] args) {
        ThreadPoolExecutor executor = null;
        try {
            //创建了一个线程池      核心线程2   最大线程5    阻塞队列容量3  空闲线程存活5s    默认的线程工厂和拒绝策略
            executor = new ThreadPoolExecutor(2, 5,
                    5, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>(3),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.AbortPolicy());
            for (int i = 0; i < 8; i++) {
                executor.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "处理业务............");
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            executor.shutdown();
        }
    }
}

我们创建了一个核心线程2,最大线程5,阻塞队列容量3 ,空闲线程存活5s 和使用默认的线程工厂和拒绝策略的线程池。

执行过程:

for循环一个创建了8个任务

前面两个任务创建了线程1和线程2

执行第三个任务时,由于核心线程数已达上限,创建了临时线程3执行

第四第五个任务创建了临时线程4和5

第六个任务时,最大线程数达到上限,丢入阻塞队列

第七第八同理丢入

等待其他线程执行完任务之后,再从阻塞队列中取出任务执行

当我们创建9个任务呢?

AbortPolicy策略

此时拒绝策略生效,AbortPolicy会直接抛出异常,并且把这个任务丢弃

拒绝策略 是否抛异常 是否丢弃任务 说明
AbortPolicy 直接抛异常,需要上层处理
CallerRunsPolicy 调用者线程执行,不会丢
DiscardPolicy 静默丢弃,无感知
DiscardOldestPolicy 丢弃最老的,提交新的

CallerRunsPolicy策略

CallerRunsPolicy会让调用者线程执行任务,但是main方法会被阻塞,会在所有任务提交完成(剩余任务能被线程池里面的线程处理的时候)后停止阻塞

java 复制代码
package com.qcby.pool;

import java.util.concurrent.*;

/**
 * 玩线程池
 * 模拟银行办理业务场景
 */
public class Demo2 {

    public static void main(String[] args) {
        ThreadPoolExecutor executor = null;
        try {
            //创建了一个线程池      核心线程2   最大线程5    阻塞队列容量1  空闲线程存活5s    默认的线程工厂和拒绝策略
            executor = new ThreadPoolExecutor(2, 5,
                    5, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>(3),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.CallerRunsPolicy());
            for (int i = 0; i < 20000; i++) {
                executor.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "处理业务............");
                });
            }

            System.out.println("任务执行完了..........");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            executor.shutdown();
        }
    }
}

我们创建了20000个任务:

DiscardPolicy策略

java 复制代码
package com.qcby.pool;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 玩线程池
 * 模拟银行办理业务场景
 */
public class Demo2 {

    public static void main(String[] args) {
        ThreadPoolExecutor executor = null;
        try {
            //创建了一个线程池      核心线程2   最大线程5    阻塞队列容量1  空闲线程存活5s    默认的线程工厂和拒绝策略
            executor = new ThreadPoolExecutor(2, 5,
                    5, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>(3),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.DiscardPolicy());
            AtomicInteger res = new AtomicInteger();
            for (int i = 0; i < 8; i++) {
                executor.execute(() -> {
                    synchronized (res) {
                        res.getAndIncrement();
                        System.out.println(Thread.currentThread().getName() + "处理业务............" + res);
                    }
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            executor.shutdown();
        }
    }
}

正常情况:

当我们触发静默丢弃策略时:

得到的结果是相同的,它把剩下的两个任务丢弃了

我们换成CallerRunsPolicy之后:

DiscardOldestPolicy

DiscardOldestPolicy会丢弃最先进入阻塞队列的任务

java 复制代码
package com.qcby.pool;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 玩线程池
 * 模拟银行办理业务场景
 */
public class Demo2 {

    public static void main(String[] args) {
        ThreadPoolExecutor executor = null;
        try {
            //创建了一个线程池      核心线程2   最大线程5    阻塞队列容量1  空闲线程存活5s    默认的线程工厂和拒绝策略
            executor = new ThreadPoolExecutor(2, 5,
                    5, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>(3),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.DiscardOldestPolicy());
            for (int i = 0; i < 100; i++) {
                Object finalI = i;
                executor.execute(() -> {
                    synchronized (finalI) {
                        System.out.println(Thread.currentThread().getName() + "处理业务............" + finalI);
                    }
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            executor.shutdown();
        }
    }
}

结果:

复制代码
pool-1-thread-1处理业务............0
pool-1-thread-2处理业务............1
pool-1-thread-5处理业务............7
pool-1-thread-3处理业务............5
pool-1-thread-2处理业务............53
pool-1-thread-1处理业务............47
pool-1-thread-4处理业务............6
pool-1-thread-2处理业务............72
pool-1-thread-3处理业务............71
pool-1-thread-5处理业务............55
pool-1-thread-3处理业务............79
pool-1-thread-5处理业务............81
pool-1-thread-1处理业务............80
pool-1-thread-3处理业务............85
pool-1-thread-4处理业务............83
pool-1-thread-5处理业务............82
pool-1-thread-1处理业务............86
pool-1-thread-3处理业务............87
pool-1-thread-4处理业务............89
pool-1-thread-1处理业务............88
pool-1-thread-2处理业务............90
pool-1-thread-1处理业务............91
pool-1-thread-5处理业务............93
pool-1-thread-1处理业务............94
pool-1-thread-5处理业务............95
pool-1-thread-4处理业务............96
pool-1-thread-1处理业务............97
pool-1-thread-5处理业务............98
pool-1-thread-3处理业务............99

后面92个任务不断触发拒绝策略,挤掉了队列中的老任务 最后执行的 99、98、97 等,是最后被保留的新任务 拒绝任务时静默丢弃,不会抛出异常。

最大线程数量怎么定?

1.CPU密集型,服务器有几核就用几个线程,在线程池构造器里面传入:

复制代码
Runtime.getRuntime().availableProcessors()

获取CPU核数

2.IO密集型,判断有哪些线程执行时很经常进行IO操作的,最大线程数量大于其即可

相关推荐
红云梦2 小时前
互联网三高-高性能之多级缓存架构
java·redis·缓存·架构·cdn
qwehjk20082 小时前
分布式计算C++库
开发语言·c++·算法
m0_716765232 小时前
C++提高编程--仿函数、常用遍历算法(for_each、transform)详解
java·开发语言·c++·经验分享·算法·青少年编程·visual studio
枫叶丹42 小时前
【HarmonyOS 6.0】ArkData 应用间配置共享:构建跨应用协作新范式
开发语言·华为·harmonyos
寻寻觅觅☆2 小时前
东华OJ-基础题-59-倒数数列(C++)
开发语言·c++·算法
我不是懒洋洋2 小时前
【数据结构】顺序表专题(详细代码及配图)
c语言·开发语言·数据结构·算法·青少年编程·visual studio
Java源码jdk2 小时前
基于javaweb和mysql的springboot校园二手书交易管理系统(java+springboot+vue+elementui+layui+mysql)
java·spring boot·mysql
listhi5202 小时前
基于在线优化的快速模型预测控制(Fast Online MPC)MATLAB实现
开发语言·matlab
毕设源码-邱学长2 小时前
【开题答辩全过程】以 校园博客系统 为例,包含答辩的问题和答案
java