记录线程池的两种实现方式

一、springboot默认的线程池@Async

个人认为,比较适合进行简单的异步调用,很方便。

@Async异步方法默认使用Spring创建ThreadPoolTaskExecutor。默认核心线程数:8,最大线程数:Integet.MAX_VALUE,队列使用LinkedBlockingQueue,容量是:Integet.MAX_VALUE,空闲线程保留时间:60s,线程池拒绝策略:AbortPolicy。

第一步 创建AsyncConfig配置类

代码片.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

/**
 * 此类使定时任务多线程异步执行
 * 在定时任务的类或者方法上添加@Async ,可使每一个任务都是在不同的线程中
 * @author sun
 */
@Configuration
@EnableAsync
public class AsyncConfig {
    /**
     * 此处成员变量应该使用@Value从配置中读取
     */
    private int corePoolSize = 30;
    private int maxPoolSize = 50;
    private int queueCapacity = 10;

    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        // 队列大小
        executor.setQueueCapacity(queueCapacity);
        executor.initialize();
        return executor;
    }
}

@Configuration注解,服务启动进行扫描,注册Bean(taskExecutor)
@EnableAsync注解,可以异步执行,差不多是开启多线程的意思

第二步 在需要异步执行的方法上加上@Async注解

代码片.

/**
     * 异步执行
     *
     * @param updater 1
     */
    @Async("taskExecutor")
    public synchronized void notify(OrderUpdater updater) {
        if (observers.isEmpty()) {
            return;
        }
        for (OrderObserver observer : observers) {
            try {
                observer.doSomeThing(updater);
            } catch (Exception e) {
                logger.error("通知失败", e);
            }
        }
    }

可以在@Async注解上指定异步线程池Bean的名字(“taskExecutor”)

注意:需要异步调用的方法notify与调用者不能在同一个class里面,否则异步方法不会生效!

二、使用ThreadPoolExecutor

第一步 创建ThreadPoolManager类配置单例模式线程池

代码片.

import lombok.extern.slf4j.Slf4j;

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

/**
 * 单例模式线程池
 *
 * @author sun
 */
@Slf4j
public class ThreadPoolManager {
    /**
     * 针对数据量大的结果进行多线程处理,最大线程数
     * <p>
     * Nthreads=CPU数量
     * Ucpu=目标CPU的使用率,0<=Ucpu<=1
     * W/C=任务等待时间与任务计算时间的比率
     * <p>
     * Nthreads =Ncpu*Ucpu*(1+W/C)
     * 针对数据量大的结果进行多线程处理,固定线程数
     */
    private static final int THREAD_NUM = 80;
    private static final int MAX_THREAD_NUM = 150;

    private ExecutorService executorService;
    private int executeThreadNum = 1;


    private ThreadPoolManager() {
        if (executorService == null) {
            // AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作;
            // CallerRunsPolicy策略:如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行;
            // DiscardOledestPolicy策略:该策略会丢弃任务队列中最老的一个任务,也就是当前任务队列中最先被添加进去的,马上要被执行的那个任务,并尝试再次提交;
            // DiscardPolicy策略:该策略会默默丢弃无法处理的任务,不予任何处理。当然使用此策略,业务场景中需允许任务的丢失;
            executorService = new ThreadPoolExecutor(THREAD_NUM, MAX_THREAD_NUM, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100),
                    r -> {
                        //线程命名
                        return new Thread(r, "threadPool" + r.hashCode());
                    }, new ThreadPoolExecutor.CallerRunsPolicy());
        }
    }

    private static ThreadPoolManager instancce;

    public static ThreadPoolManager getInstancce() {
        if (instancce == null) {
            instancce = new ThreadPoolManager();
        }
        return instancce;
    }

    public void executeTask(Runnable runnable) {
        executorService.execute(runnable);
        log.debug("异步线程执行了{}次", executeThreadNum++);
    }
}

第二步 使用线程池

代码片.

for (int i = 0; i < loopNum; i++) {
    ThreadPoolManager.getInstancce().executeTask(() -> {
        try {
            // 让线程阻塞,使后续任务进入缓存队列
            Thread.sleep(1000);
            // do something
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值