线程池

线程池

“线程池(英语:thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。但是阿里的开发手册中不推荐使用Executors 来创建线程池

阿里开发手册摘取内容如下:

【强制】 线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这
样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明: Executors 返回的线程池对象的弊端如下:
	1) FixedThreadPool 和 SingleThreadPool:
		允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM(内存溢出)。
	2) CachedThreadPool:
		允许的创建线程数量为 Integer.MAX_VALUE, 可能会创建大量的线程,从而导致 OOM(内存溢出)。

Executors 工具类

常用方法(创建线程池的方法)

返回值 方法名 说明
ExecutorService newFixedThreadPool(int nThreads) 创建一个线程池对象, nThreads是线程池内的线程最大数量

例如创建线程池: ExecutorService pool = Executors.newFixedThreadPool(3);

实例代码:

// 创建线程池对象 -- 参数是线程池的大小(线程数)
ExecutorService pool = Executors.newFixedThreadPool(3);

// 利用线程池执行线程任务
// execute(); 参数需要Runnable接口类型
for (int i = 0; i < 3; i++) {
    pool.execute(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + " : " + i);
            }
        }
    });
}

ThreadPoolExecutor创建线程池

构造方法

方法 说明
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
corePoolSize 核心线程数
maximumPoolSeze 最大线程数
keepAliveTime 线程空闲多长时间被释放
unit 时间单位
wordQueue 任务队列
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
与上面构造方法多了一个参数
threadFactory线程工厂
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
handler拒绝执行任务的一种策略
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
包含了上面所有的参数

其最大吞吐量为: 最大线程数 + 最大队列数

代码案例-简单案例

public static void main(String[] args) {
    // 任务队列. 这里设置为2
    BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
    // 定义线程工厂, 可以自动以线程名字前缀
    ThreadFactory threadFactory = new ThreadFactory() {
        AtomicLong al = new AtomicLong(1); // 自动递增策略, 从1开始
        @Override
        public Thread newThread(Runnable r) {
            String name = "cgb2004-thread-" + al.getAndIncrement(); // 先获取值, 再自增
            return new Thread(r, name);
        }
    };
    ThreadPoolExecutor tExecutor = new ThreadPoolExecutor(
        2, // corePoolSize 核心线程数
        3, // maximumPoolSeze 最大线程数
        60, // keepAliveTime 线程空闲多长时间被释放
        TimeUnit.SECONDS, // unit 时间单位
        workQueue, // wordQueue 任务队列
        threadFactory, // threadFactory 线程工厂
        new CallerRunsPolicy()); // 拒绝执行任务的一种策略

    tExecutor.execute(new Runnable() { // 从线程池获取线程
        @Override
        public void run() {
            String tName = Thread.currentThread().getName();
            System.out.println(tName + "->task-01");
            // try{Thread.sleep(10000);} catch(Exception e) {}
        }
    });
    tExecutor.execute(new Runnable() { // 从线程池获取线程
        @Override
        public void run() {
            String tName = Thread.currentThread().getName();
            System.out.println(tName + "->task-02");
            // try{Thread.sleep(10000);} catch(Exception e) {}
        }
    });
    // tExecutor.execute(new Runnable()... 
}

执行流程描述

首先, 核心线程数corePoolSize 即线程池中一直存在线程的数量, 当需要时会从里面取出线程使用

当核心线程都在使用中, 那么就会加入到消息队列中如, 等核心线程空闲的时候, 队列中的任务才能执行

当核心线程都在使用中, 且消息队列也达到了最大上限, 这时候又有任务来了, 那么就会创建新的线程, 来执行, 前提是线程池中的线程数量没有超过最大线程数

如果说最大线程数也满了, 这时候又来了新的任务, 这时候会拒绝执行, 抛出异常, 当然, 我们也可以使用构造器中的RejectedExecutionHandler handler借口自定义拒绝执行的的策略, 例如传入一个new CallerRunsPolicy() 参数, 来让main线程执行等

posted @ 2020-05-28 19:15  zpk-aaron  阅读(162)  评论(0编辑  收藏  举报