Java多线程批量处理数据

package com.demo.studydemo.test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/**
 * 批量数据处理示例类。
 * 该类演示了如何通过多线程批量处理数据,将数据集分割成多个部分,每个部分由一个单独的线程进行处理。
 * @author lhj
 * @date 2024/04/26
 */
public class BatchDataProcessingExample {

    /**
     * 程序的主入口函数。
     * @param args 命令行参数(未使用)
     * @throws InterruptedException 如果线程在等待时被中断,则抛出此异常。
     */
    public static void main(String[] args) throws InterruptedException {
        // 初始化数据处理参数
        int dataSize = 1_000_000; // 需要处理的数据量
        int batchSize = 100_000; // 每个线程处理的数据量
        int threadCount = (dataSize + batchSize - 1) / batchSize; // 根据数据量和批量大小计算所需线程数
        System.out.println("需要启动" + threadCount + "个线程处理数据");

        // 模拟数据集
        List<Integer> data = new ArrayList<>(dataSize);
        for (int i = 0; i < dataSize; i++) {
            data.add(i);
        }

        // 使用CountDownLatch来同步线程
        CountDownLatch latch = new CountDownLatch(threadCount);

        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);

        // 分配任务到线程池
        for (int i = 0; i < threadCount; i++) {
            int start = i * batchSize;
            int end = Math.min(start + batchSize, dataSize);
            List<Integer> subList = data.subList(start, end);
            executor.submit(new DataProcessor(subList, latch));
        }

        // 等待所有任务完成
        latch.await();
        System.out.println("所有数据处理完成");

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

    /**
     * 数据处理任务类,实现了Runnable接口。
     */
    static class DataProcessor implements Runnable {
        private final List<Integer> dataList; // 待处理的数据列表
        private final CountDownLatch latch; // 线程同步的计数器

        /**
         * DataProcessor 构造函数。
         * @param dataList 待处理的数据列表。
         * @param latch 线程同步的计数器。
         */
        public DataProcessor(List<Integer> dataList, CountDownLatch latch) {
            this.dataList = dataList;
            this.latch = latch;
        }

        /**
         * 执行数据处理的方法。
         */
        @Override
        public void run() {
            processData(dataList);
            // 任务完成,计数器减一
            latch.countDown();
            System.out.println("当前线程"+ Thread.currentThread().getName() +"完成数据处理");
        }

        /**
         * 示例数据处理方法。
         * @param sublist 待处理的数据子列表。
         */
        private void processData(List<Integer> sublist) {
            // 简化处理逻辑,仅打印处理的数据范围
            System.out.println("线程 " + Thread.currentThread().getName() + " 正在处理数据范围: " + sublist.get(0) + " 到 " + sublist.get(sublist.size() - 1));
            // 可以在此处添加实际的数据处理逻辑
        }
    }
}


Java中,通过`ExecutorService`接口创建的线程池提供了两种主要的方法来提交任务:`submit()`和`execute()`。这两种方法都可以用来执行`Runnable`或`Callable`任务,但它们之间存在一些差异:

1. **execute()**- **参数**:接受一个`Runnable`类型的任务。
   - **返回值**:`void`,也就是说它不返回任何结果。
   - **异常处理**:它不提供直接获取任务执行时抛出异常的机制。如果任务执行期间抛出了异常,它会被传递给线程池的`uncaughtExceptionHandler`处理,而不是直接暴露给调用者。
   - **用途**:通常用于不需要关注任务执行结果的场景,或者通过其他方式(如Future、回调等)来处理结果或异常。

2. **submit()**- **参数**:可以接受`Runnable`或`Callable`类型的任务。如果传入的是`Callable`,那么`submit()`会返回一个`Future`对象。
   - **返回值**:对于`Runnable`,返回一个表示任务执行完成的`Future<Void>`;对于`Callable`,返回一个`Future<T>`,其中`T`是`Callable`任务返回的结果类型。
   - **异常处理**:如果任务执行期间抛出了异常,这个异常会被封装进返回的`Future`对象中,调用者可以通过`Future.get()`方法来获取异常或结果,从而可以更精细地控制异常处理逻辑。
   - **用途**:适用于需要获取任务执行结果或需要处理任务执行过程中可能抛出异常的场景。`Future`提供了检查任务是否完成、取消任务以及获取结果的方法。

**总结**- 如果你不需要关心任务的执行结果,或者任务没有结果需要返回,可以使用`execute()`。
- 如果你需要获得任务的执行结果,或者需要能够取消任务、检查任务状态等高级功能,应该使用`submit()`,尤其是当你提交的是`Callable`任务时。通过`submit()`得到的`Future`对象为你提供了更多的灵活性和控制力。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是Java多线程批量修改数据的示例代码: ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class BatchUpdateDemo { private static final String URL = "jdbc:mysql://localhost:3306/test"; private static final String USER = "root"; private static final String PASSWORD = "123456"; public static void main(String[] args) { Connection conn = null; PreparedStatement pstmt = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(URL, USER, PASSWORD); conn.setAutoCommit(false); String sql = "update user set age = ? where id = ?"; pstmt = conn.prepareStatement(sql); // 模拟需要修改的数据 int[] ids = {1, 2, 3, 4, 5}; int[] ages = {20,21, 22, 23, 24}; // 创建线程池 ExecutorService executorService = Executors.newFixedThreadPool(5); // 批量修改数据 for (int i = 0; i < ids.length; i++) { int id = ids[i]; int age = ages[i]; executorService.execute(() -> { try { pstmt.setInt(1, age); pstmt.setInt(2, id); pstmt.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } }); } // 关闭线程池 executorService.shutdown(); while (!executorService.isTerminated()) { Thread.sleep(100); } // 提交事务 conn.commit(); } catch (ClassNotFoundException | SQLException | InterruptedException e) { e.printStackTrace(); try { if (conn != null) { conn.rollback(); } } catch (SQLException ex) { ex.printStackTrace(); } } finally { try { if (pstmt != null) { pstmt.close(); } if (conn != null) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } } ``` 该示例代码使用了线程池来批量修改数据,通过创建一个固定大小的线程池,将每个修改操作封装成一个任务,交给线程池去执行。这样可以避免频繁地创建和销毁线程,提高了程序的性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值