11、Spring 多线程 ThreadPoolTaskExecutor 实例

继续上集SSM集成swagger 和 log4j,这次需要实现在service里面并行插入1000条数据,在全部完成后返回结果

一、添加 'ThreadPoolTaskExecutor' Bean

1、新增配置类ExcutorConfig

@EnableAsync
@Configuration
public class ExcutorConfig {
    private static Logger logger = Logger.getLogger(ExcutorConfig.class);

    @Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        logger.info("start executor -->");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //设置核心线程数
        executor.setCorePoolSize(50);
        //设置最大线程数
        executor.setMaxPoolSize(300);
        //设置队列大小
        executor.setQueueCapacity(300);
        //配置线程池的前缀
        executor.setThreadNamePrefix("async-service-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //设置空闲时间
        executor.setKeepAliveSeconds(60);
        //进行加载
        executor.initialize();
        return executor;
    }
}

这里故意把线程池的数目设置的比较大
注意: 这里需要在 spring-mvc的配置中把config类的报名注册到component-scan里面,修改后如下:

image.png

2、如果不想通过代码,则在spring-service中新增如下配置即可

<bean id="asyncServiceExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
         <!-- 核心线程数 -->
        <property name="corePoolSize" value="50" />
        <!-- 最大线程数 -->
        <property name="maxPoolSize" value="300" />
        <!-- 队列最大长度 >=mainExecutor.maxSize -->
        <property name="queueCapacity" value="300" />
        <!-- 线程池维护线程所允许的空闲时间 -->
        <property name="keepAliveSeconds" value="60" />
        <!-- 线程池对拒绝任务(无线程可用)的处理策略 ThreadPoolExecutor.CallerRunsPolicy策略 ,调用者的线程会执行该任务,如果执行器已关闭,则丢弃.  -->
        <property name="rejectedExecutionHandler">
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
        </property>
</bean>


二、编写service

1、修改RoleService和实现类,添加如下接口

@Override
    public int insertRole(Role role) {
        return roleMapper.insert(role);
    }

接受一个 Role 对象,直接插入到数据表中

2、新增一个TaskExecuterTestService,用于实现并行插入N条记录

接口

public interface TaskExecuterTestService {

     void insertRoles(List<Role> roles);
}

实现类

@Service
public class TaskExecuterTestServiceImpl implements TaskExecuterTestService {

    private Logger logger = Logger.getLogger(this.getClass());

    @Resource(name = "asyncServiceExecutor")
    private ThreadPoolTaskExecutor taskExecutor;

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void insertRoles(List<Role> roles) {
        CountDownLatch countDownLatch = new CountDownLatch(1000);
        for (int i = 0; i < roles.size(); i++) {
            Role role = roles.get(i);
            taskExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        RoleService roleService = applicationContext.getBean(RoleService.class);
                        int ret = roleService.insertRole(role);
                        System.out.println("插入Role[" + role.getRoleName()+"]结果: " + ret + ",    当前线程id:  " + Thread.currentThread().getId());
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        countDownLatch.countDown();  //这个不管是否异常都需要数量减,否则会被堵塞无法结束
                    }
                }
            });
        }
        try {
            countDownLatch.await(); //保证之前的所有的线程都执行完成,才会走下面的;
            // 这样就可以在下面拿到所有线程执行完的集合结果
            System.out.println("all roles insert done..........................................................");
        } catch (Exception e) {
            logger.error("阻塞异常");
        }
    }
}

注意事项

  • 因为想要每个最终执行插入的逻辑由不同的数据库交互service去做,所以这里需要手动在每个Task里面去获取独立的service bean
    image.png
  • 使用CountDownLatch类来实现主线程的等待,在所有子线程工作完成前,主线程会一直等待在下面位置:
    image.png

    CountDownLatch类的详解可以参考文章CountDownLatch详解
  • 这里用的是@Resource来注入的ThreadPoolTaskExecutorbean,是采用的类配置方式;若使用的xml,可以直接用@Autowired即可

三、编写Controller

新增一个TaskExecuterTestController用作入口

@Controller
@RequestMapping("/task")
@Api(value = "/task", tags = {"TaskExecuter测试"})
public class TaskExecuterTestController {

    @Autowired
    private TaskExecuterTestService taskExecuterTestService;

    @RequestMapping(value = "/doTest", method = RequestMethod.GET)
    @ResponseBody
    public String doTest() {
        StopWatch sc = new StopWatch();
        sc.start();
        SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
        List<Role> roles = new ArrayList<Role>();
        int totalCount = 100000;
        for (int i = 0; i < totalCount; i++) {
            Role role = new Role();
            role.setId(UUID.randomUUID().toString());
            role.setRoleName("testRole_" + i);
            role.setNote(ft.format(new Date()));
            roles.add(role);
        }
        taskExecuterTestService.insertRoles(roles);
        sc.stop();
        return "测试完成,插入数据 "+totalCount+" 条,总耗时 " + sc.getTotalTimeMillis() + " 毫秒";
    }
}

逻辑很简单,生成100000个role对象,然后调用service并行插入数据库,返回总耗时


四、测试

1、Swagger直接运行

初始数据库记录如下:


image.png

2、Swagger运行结果

image.png

插入数据10万条,总耗时 25 s

3、IDEA log

image.png

4、数据库记录

image.png


五、本次修改代码变更

Github-Commit spring多线程 TaskExecuter测试

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,444评论 4 365
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,867评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,157评论 0 248
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,312评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,673评论 3 289
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,802评论 1 223
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,010评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,743评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,470评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,696评论 2 250
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,187评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,538评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,188评论 3 240
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,127评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,902评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,889评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,741评论 2 274