多线程批量处理数据

一、前言

在项目开发中有时会遇到批量操作,如果操作时间较长,我们可能会使用多线程来处理。下面举一个我在实际项目开发过程中使用多线程优化的一个过程。

二、业务线程类
package thread;

import lombok.SneakyThrows;

import java.util.concurrent.Callable;

/**
 * @Description 业务线程类
 * @Author King
 * @Date 2020/4/22 14:24
 * @Version 1.0
 **/
public class BusinessThread implements Callable<String> {

    private String name;

    BusinessThread(String name) {
        this.name = name;
    }

    @SneakyThrows
    @Override
    public String call() {

        // 模拟处理业务逻辑
        System.out.println(name + "正在游戏中--" + Thread.currentThread().getName());

        Thread.sleep(1000);

        return name;
    }
}
三、多线程优化流程
package thread;

import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.SneakyThrows;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Description 多线程优化流程
 * @Author King
 * @Date 2020/4/22 15:01
 * @Version 1.0
 **/
public class Test {

    /**
     * 自定义线程工厂,可以设置线程名前缀、是否守护线程、优先级等属性
     */
    private static ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("king-%d").build();

    /**
     * 在开发过程中最好自定义线程池 根据实际情况设置核心线程、最大线程、存活时间、工作队列、线程工厂、拒绝策略
     * 线程池执行流程
     * 核心线程--》工作队列--》最大线程--》拒绝策略
     */
    private static ExecutorService executor = new ThreadPoolExecutor(3, 5, 1,
            TimeUnit.MINUTES, new ArrayBlockingQueue<>(5), factory);

    private static List<String> names = Lists.newArrayList("鲁班", "虞姬", "蒙犽", "后裔");

    public static List<String> result = Lists.newArrayList();

    public static void main(String[] args) {

        // 初版
        System.out.println("======================初版=======================");

        firstVersion();

        // 更新版
        System.out.println("======================更新版=======================");

        updateVersion();

        // 最终版
        System.out.println("======================最终版=======================");

        finalVersion();

        // 关闭线程池
        executor.shutdown();

    }

    /**
     * 初版
     */
    private static void firstVersion() {
        names.forEach(n -> {

            Future<String> future = executor.submit(new BusinessThread(n));

            try {

                // future.get()方法是一个阻塞方法。等待计算完成才能处理结果
                addAndPrint(future.get());

            } catch (Exception e) {

                System.out.println(n + "处理失败");
            }
        });
    }

    /**
     * 更新版
     */
    @SneakyThrows
    private static void updateVersion() {

        List<Future<String>> futures = new ArrayList<>();

        // 将所有任务提交给线程池处理
        names.forEach(n -> futures.add(executor.submit(new BusinessThread(n))));

        while (futures.size() > 0) {

            Iterator<Future<String>> iterator = futures.iterator();

            while (iterator.hasNext()) {

                Future<String> future = iterator.next();

                // 当任务执行完成后取出
                if (future.isDone() && !future.isCancelled()) {

                    addAndPrint(future.get());

                    iterator.remove();

                } else {
                    // 避免CPU高速运转
                    Thread.sleep(10);
                }
            }
        }
    }

    /**
     * 最终版
     */
    private static void finalVersion() {

        // JDK8 CompletableFuture
        // 满足并发执行,顺序完成并按先后顺序获取的目标
        CompletableFuture[] cfs = names.stream().map(n ->

                // 申请一个异步任务,执行自定义方法,放入自定义线程池内
                CompletableFuture.supplyAsync(() -> handle(n), executor)

                        // 当任务完成时可对任务进行处理,支持任务执行中的异常处理
                        .whenComplete((r, e) -> addAndPrint(r))).toArray(CompletableFuture[]::new);

        // 任务开始执行
        CompletableFuture.allOf(cfs).join();
    }

    /**
     * 线程类的实现逻辑转为方法
     */
    @SneakyThrows
    private static String handle(String name) {
        // 模拟处理业务逻辑
        System.out.println(name + "正在游戏中--" + Thread.currentThread().getName());

        Thread.sleep(1000);

        return name;
    }

    /**
     * 添加并打印
     */
    private static void addAndPrint(String name) {

        result.add(name);
        System.out.println(name + "已经超神啦");

    }
}
五、执行结果

在这里插入图片描述

六、总结
1、初版:虽然使用了多线程,但由于Future.get()方法是阻塞方法,同时又在for循环中获取,所以每次线程池内只有一个线程执行。相比不使用线程,只算是异步执行。
需解决问题:多个任务同时执行
2、更新版: 解决了线程池内只有一个任务执行的问题。但由于在获取结果时使用while,造成了CPU高速运转。
需解决问题: 高CPU使用率
3、最终版:使用JDK8新接口CompletableFuture,支持多个任务同时执行,支持源生API,支持异常处理,完美!!!
  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中可以使用ThreadPoolTaskExecutor来实现多线程处理批量数据入库的操作。ThreadPoolTaskExecutor是Spring框架提供的一个线程池实现类,可以方便地管理线程池的创建和销毁,并提供了一些配置参数来控制线程池的行为。 下面是使用ThreadPoolTaskExecutor实现多线程处理批量数据入库的步骤: 1. 首先,需要在项目中引入Spring的依赖,以使用ThreadPoolTaskExecutor类。可以在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> ``` 2. 在代码中创建一个ThreadPoolTaskExecutor对象,并进行相关配置。可以通过在Spring配置文件中配置bean,或者使用Java代码进行配置。以下是一个示例配置: ```java @Configuration @EnableAsync public class ThreadPoolConfig { @Bean public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); // 设置核心线程数 executor.setMaxPoolSize(20); // 设置最大线程数 executor.setQueueCapacity(100); // 设置队列容量 executor.setThreadNamePrefix("MyThread-"); // 设置线程名前缀 executor.initialize(); // 初始化线程池 return executor; } } ``` 3. 在需要进行批量数据入库的地方,使用@Async注解标记方法,并指定使用的线程池。例如: ```java @Service public class DataBatchService { @Autowired private ThreadPoolTaskExecutor taskExecutor; @Async("taskExecutor") public void processBatchData(List<Data> dataList) { // 批量数据入库的逻辑处理 // ... } } ``` 4. 调用processBatchData方法时,会自动使用线程池中的线程进行处理。例如: ```java @Autowired private DataBatchService dataBatchService; public void batchDataInsert(List<Data> dataList) { dataBatchService.processBatchData(dataList); } ``` 这样就可以利用ThreadPoolTaskExecutor实现多线程处理批量数据入库了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值