Java 线程池规范使用示例

1 摘要

Java 线程池在批量处理的场景中应用广泛。常见的四种创建线程池的方法:newSingleThreadExecutor,newFixedThreadPool,newCachedThreadPool,newScheduledThreadPool,各有各的缺陷,不建议使用。本文将给出根据阿里巴巴Java开发手册推荐的方式创建线程池的示例。

2 推荐创建线程池的方法

java.util.concurrent.ThreadPoolExecutor

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

参数说明:
corePoolSize: 线程池核心线程数量,推荐为服务器的核心数量
maximumPoolSize: 线程池最大线程数量,推荐为和服务核心数量的两倍
keepAliveTime: 线程销毁等待时间,当线程数超过核心线程数量(corePoolSize)时,销毁多余线程需要等待的最大时间;可根据任务执行的频率设置时间长度
unit: 线程销毁等待时间单位,有毫秒,秒,小时等
workQueue: 工作负载阻塞队列,当线程池中线程数量达到最大值时,仍然有未处理的任务,此时会将任务放在该队列中
threadFactory: 线程工厂,用于创建线程
RejectedExecutionHandler: 线程池拒绝策略,当线程数量达到线程池最大值,工作负载队列满了之后,线程池对于新任务的处理策略。有四种:

java.util.concurrent.ThreadPoolExecutor.AbortPolicy: 抛出异常,不作处理
java.util.concurrent.ThreadPoolExecutor.DiscardPolicy: 丢弃新任务,也不抛出异常
java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy: 丢弃最老的任务
java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy: 把任务交给调用线程池的线程处理,直到线程池死了

通常情况下选择抛出异常的策略即可。

3 核心示例代码

3.1 任务负载
./src/main/java/com/ljq/demo/concurrent/DrinkTask.java
package com.ljq.demo.concurrent;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Callable;

/**
 * @Description: 喝酒任务
 * @Author: junqiang.lu
 * @Date: 2021/5/14
 */
@Slf4j
public class DrinkTask implements Callable<Integer> {

    /**
     * 当前杯数
     */
    private int cupNumber;

    public DrinkTask(int cupNumber) {
        this.cupNumber = cupNumber;
    }

    @Override
    public Integer call() throws Exception {
        log.info("武松喝完了第[" + cupNumber + "]杯酒");
        Thread.sleep(100);
        return cupNumber;
    }
}
3.2 简易线程池工具类
./src/main/java/com/ljq/demo/util/ThreadPoolUtil.java
package com.ljq.demo.util;

import cn.hutool.core.thread.ThreadFactoryBuilder;

import java.util.concurrent.*;

/**
 * @Description: 线程池工具类
 * @Author: junqiang.lu
 * @Date: 2021/5/14
 */
public class ThreadPoolUtil {

    private ThreadPoolUtil(){
    }

    static {
        initExecutor();
    }

    /**
     * 线程池执行器
     */
    private static transient ThreadPoolExecutor executor;

    /**
     * 执行任务
     * (不包含返回值)
     *
     * @param command
     */
    public static void execute(Runnable command) {
        executor.execute(command);
    }

    /**
     * 执行任务
     * (包含返回值)
     *
     * @param task
     * @param <T>
     * @return
     */
    public static <T> Future<T> executeAsync(Callable<T> task) {
        return executor.submit(task);
    }

    /**
     * 销毁线程池
     */
    public static void shutdown() {
        executor.shutdown();
    }

    /**
     * 初始化线程池
     */
    private static void initExecutor() {
        if (executor == null || executor.isShutdown()) {
            synchronized (ThreadPoolUtil.class) {
                if (executor == null || executor.isShutdown()) {
                    executor = new ThreadPoolExecutor(4,10,100,
                            TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1000),
                            ThreadFactoryBuilder.create().build(), new ThreadPoolExecutor.AbortPolicy());
                }
            }
        }
    }


}

3.3 线程池运行测试示例
./src/main/java/com/ljq/demo/concurrent/DrinkThreadPoolDemo.java
package com.ljq.demo.concurrent;

import cn.hutool.core.thread.ThreadFactoryBuilder;
import cn.hutool.core.thread.ThreadUtil;
import com.ljq.demo.util.ThreadPoolUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Description: 喝酒线程池示例
 * @Author: junqiang.lu
 * @Date: 2021/5/14
 */
@Slf4j
public class DrinkThreadPoolDemo {

    public static void main(String[] args) {
        manualThreadPool();
        huToolThreadUtil();
        localThreadPoolUtil();
    }

    /**
     * 手动创建线程池
     */
    public static void manualThreadPool() {
        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2,10,100,
                TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1000), ThreadFactoryBuilder.create().build(),
                new ThreadPoolExecutor.AbortPolicy());
        // 总杯数
        int totalCup = 100;
        int cupNumber;
        for (int i = 1; i <= totalCup; i++) {
            try {
                cupNumber = executor.submit(new DrinkTask(i)).get();
                log.info("manual cupNumber: {}", cupNumber);
            } catch (InterruptedException e) {
                log.error("线程池执行错误", e);
            } catch (ExecutionException e) {
                log.error("武松喝醉了", e);
            }
        }
        // 关闭线程池
        executor.shutdown();
    }

    /**
     * 使用 hutool 线程工具类
     * (线程池不会自动销毁)
     */
    public static void huToolThreadUtil() {
        // 总杯数
        int totalCup = 100;
        int cupNumber;
        for (int i = 1; i <= totalCup; i++) {
            try {
                cupNumber = ThreadUtil.execAsync(new DrinkTask(i)).get();
                log.info("hutool cupNumber: {}", cupNumber);
            } catch (InterruptedException e) {
                log.error("线程池执行错误", e);
            } catch (ExecutionException e) {
                log.error("武松喝醉了", e);
            }
        }
    }

    /**
     * 本地线程池工具类
     */
    public static void localThreadPoolUtil() {
        // 总杯数
        int totalCup = 100;
        int cupNumber;
        for (int i = 1; i <= totalCup; i++) {
            try {
                cupNumber = ThreadPoolUtil.executeAsync(new DrinkTask(i)).get();
                log.info("local cupNumber: {}", cupNumber);
            } catch (InterruptedException e) {
                log.error("线程池执行错误", e);
            } catch (ExecutionException e) {
                log.error("武松喝醉了", e);
            }
        }
        ThreadPoolUtil.shutdown();

    }
}


3.4 部分执行日志
2021-05-14 16:50:58:890 [pool-4-thread-1] INFO  com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[1]杯酒
2021-05-14 16:50:58:992 [main] INFO  com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 1
2021-05-14 16:50:58:993 [pool-4-thread-2] INFO  com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[2]杯酒
2021-05-14 16:50:59:094 [main] INFO  com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 2
2021-05-14 16:50:59:095 [pool-4-thread-3] INFO  com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[3]杯酒
2021-05-14 16:50:59:199 [main] INFO  com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 3
2021-05-14 16:50:59:200 [pool-4-thread-4] INFO  com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[4]杯酒
2021-05-14 16:50:59:301 [main] INFO  com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 4
2021-05-14 16:50:59:301 [pool-4-thread-1] INFO  com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[5]杯酒
2021-05-14 16:50:59:401 [main] INFO  com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 5

... ...

2021-05-14 16:51:08:694 [pool-4-thread-4] INFO  com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[96]杯酒
2021-05-14 16:51:08:794 [main] INFO  com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 96
2021-05-14 16:51:08:795 [pool-4-thread-1] INFO  com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[97]杯酒
2021-05-14 16:51:08:897 [main] INFO  com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 97
2021-05-14 16:51:08:897 [pool-4-thread-2] INFO  com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[98]杯酒
2021-05-14 16:51:08:998 [main] INFO  com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 98
2021-05-14 16:51:08:999 [pool-4-thread-3] INFO  com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[99]杯酒
2021-05-14 16:51:09:101 [main] INFO  com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 99
2021-05-14 16:51:09:102 [pool-4-thread-4] INFO  com.ljq.demo.concurrent.DrinkTask(DrinkTask.java 26) -武松喝完了第[100]杯酒
2021-05-14 16:51:09:203 [main] INFO  com.ljq.demo.concurrent.DrinkThreadPoolDemo(DrinkThreadPoolDemo.java 82) -local cupNumber: 100

4 推荐参考示例

按照阿里巴巴规范创建Java线程池

5 Github 源码

Gtihub 源码地址 : https://github.com/Flying9001/Demo
个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.
404Code

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值