Java多线程处理大list集合(可获取返回数据)

Java多线程处理大list集合,减少耗时

问题

在我开发中遇到一个list大集合,list数据太大,通过业务层面已经做了一些优化处理,但是还未达标客户要求(要求60s内完成解析),所以只能采用线程去处理,也看了一些博主的文章,受此启发,自己也发个文章记录下。

创建线程池

我是使用的jdk自带的线程池,ThreadPoolExecutor来创建线程池

this.executorService = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);

并且配置相关的参数:线程核心数、最大线程数、空闲时间、单位、队列

    /**
     * 最大线程数量 使用cpu核心数
     */
    private static final int maximumPoolSize = Runtime.getRuntime().availableProcessors();

    /**
     * 核心线程数
     */
    private static final int corePoolSize =  maximumPoolSize / 2;

    /**
     * 空闲时间
     */
    private static final long keepAliveTime = 60;

    /**
     * 单位 秒
     */
    private static final TimeUnit  unit = TimeUnit.SECONDS;

    /**
     * 任务队列数量
     */
    private static final BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(1000);

    // 线程池实例
    private final ExecutorService executorService;

然后使用AtomicReference来存储实例保证线程安全

private static final AtomicReference<ThreadPoolUtil> INSTANCE = new AtomicReference<>();

然后使用单例模式来获取线程池实例,这里使用的是双重加锁的模式来实现单例

  // 双重检查锁定模式获取实例
    public static ThreadPoolUtil getInstance() {
        ThreadPoolUtil instance = INSTANCE.get();
        if (instance == null) {
            synchronized (ThreadPoolUtil.class) {
                instance = INSTANCE.get();
                if (instance == null) {
                    instance = new ThreadPoolUtil();
                    INSTANCE.set(instance);
                }
            }
        }
        return instance;
    }

能够让我们每次获取的时候都能获取同一个线程池实例对象,不会去重复创建线程池。然后向外提供线程池提交任务的方法,可以通过Future来获取返回内容

    // 带返回值 提交任务到线程池
    public Future<T> submitTask(Callable task) {
        return executorService.submit(task);
    }

完整代码如下:

package com.example.demo.utils;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Aescription: 单例线程池
 * @Author: huao
 * @Date: 2024/8/29 15:10
 */

@Slf4j
public class ThreadPoolUtil<T> {

    // 使用AtomicReference来存储实例,确保线程安全
    private static final AtomicReference<ThreadPoolUtil> INSTANCE = new AtomicReference<>();

    /**
     * 最大线程数量 使用cpu核心数
     */
    private static final int maximumPoolSize = Runtime.getRuntime().availableProcessors();

    /**
     * 核心线程数
     */
    private static final int corePoolSize =  maximumPoolSize / 2;

    /**
     * 空闲时间
     */
    private static final long keepAliveTime = 60;

    /**
     * 单位 秒
     */
    private static final TimeUnit  unit = TimeUnit.SECONDS;

    /**
     * 任务队列数量
     */
    private static final BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(1000);

    // 线程池实例
    private final ExecutorService executorService;

    // 私有构造函数
    private ThreadPoolUtil() {
        log.info("线程池参数:核心线程数{},最大线程数{},空闲时间{}s,队列大小{}",corePoolSize,maximumPoolSize,keepAliveTime,workQueue.size());
        this.executorService = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    // 双重检查锁定模式获取实例
    public static ThreadPoolUtil getInstance() {
        ThreadPoolUtil instance = INSTANCE.get();
        if (instance == null) {
            synchronized (ThreadPoolUtil.class) {
                instance = INSTANCE.get();
                if (instance == null) {
                    instance = new ThreadPoolUtil();
                    INSTANCE.set(instance);
                }
            }
        }
        return instance;
    }

    // 带返回值 提交任务到线程池
    public Future<T> submitTask(Callable task) {
        log.info("队列任务数量:{}",workQueue.size());
        return executorService.submit(task);
    }

    // 不带返回值 提交任务到线程池
    public void submitTask(Runnable task){
        executorService.submit(task);
    }

    // 关闭线程池
    public void shutdown() {
        executorService.shutdown();
    }

    // 尝试优雅关闭线程池,等待已提交的任务完成
    public void shutdownNow() {
        executorService.shutdownNow();
    }
}

代码测试

测试内容代码如下

package com.example.demo.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
public class Test {

    public static void main(String[] args) {
        //初始化60个数据
        List<String> list = new ArrayList<>();
        int size = 60;
        for (int i = 0; i < size; i++) {
            list.add("task-"+i);
        }
        //处理完成后的结果集
        List<String> results = new ArrayList<>();
        long millis = System.currentTimeMillis();
        System.out.println("=========不使用多线程处理数据============");
        System.out.println("===============开始解析=================");
        results.addAll(execute(list));
        System.out.println("解析完成,解析数据量:"+results.size()+",耗时:"+(System.currentTimeMillis() - millis) / 1000 + "秒");
        results.clear();
        System.out.println("===============分割线===================");
        System.out.println("===========使用多线程处理数据============");
        System.out.println("===============开始解析=================");
        long millis1 = System.currentTimeMillis();
        try {
            ThreadPoolUtil<List<String>> threadPoolUtil = ThreadPoolUtil.getInstance();
            List<Future<List<String>>> futureList = new ArrayList<>();
            //这里根据线程池的核心数去进行分段处理,最好不要一开始就达到最大线程数,只需要一半即可
            int numThreads = Runtime.getRuntime().availableProcessors() / 2;
            for (int i = 0; i < list.size(); i += list.size() / numThreads) {
                int start = i;
                int end = Math.min(i + list.size() / numThreads, list.size());
                List<String> subList = list.subList(start, end);
                Callable<List<String>> task = () -> {
                    return execute(subList);
                };
                Future<List<String>> future = threadPoolUtil.submitTask(task);
                futureList.add(future);
            }
            for (Future<List<String>> future : futureList) {
                results.addAll(future.get());
            }
        }catch (Exception e){
           e.printStackTrace();
        }
        System.out.println("解析完成,解析数据量:"+results.size()+",耗时:"+(System.currentTimeMillis() - millis1) / 1000 + "秒");

    }

    private static List<String> execute(List<String> subList){
        List<String> result = new ArrayList<>();
        for (String s : subList) {
            try {
                //模拟耗时每次1s
                Thread.sleep(1000);
                result.add(s + Thread.currentThread().getName());
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return result;
    }

}

我是初始化60条数据,每条数据处理模拟要耗时1秒,不使用线程的情况下就需要执行60秒,使用线程后可以优化到11秒。运行结果如下:

=========不使用多线程处理数据============
===============开始解析=================
解析完成,解析数据量:60,耗时:60秒
===============分割线===================
===========使用多线程处理数据============
===============开始解析=================
15:52:36.640 [main] INFO com.example.demo.utils.ThreadPoolUtil - 线程池参数:核心线程数8,最大线程数16,空闲时间60s,队列大小0
15:52:36.665 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
解析完成,解析数据量:60,耗时:11秒

有什么问题,欢迎在评论区留言。

Java中的多线程处理List集合通常用于提高程序的并发性能和资源利用率。多线程可以同时执行多个任务,而List集合提供了有序的数据结构,适合在并发环境中进行读写操作。以下是使用多线程处理List的一些关键点: 1. **线程安全**:对于List集合,如果要在多线程环境下直接修改(如添加、删除元素),应使用`Collections.synchronizedList(list)`确保线程安全,这样每次修改都会自动同步。 2. **CopyOnWriteArrayList**:`CopyOnWriteArrayList`是一个线程安全的列表实现,当线程试图修改时,会创建一个新的列表副本进行修改,避免了同步带来的性能开销,但不适合频繁的随机访问。 3. **并发包的工具类**:`java.util.concurrent`包中提供了`ConcurrentHashMap`和`BlockingQueue`等线程安全的数据结构,这些可以用来配合多线程操作List,例如用`BlockingQueue`作为缓冲区,处理线程间的数据交换。 4. **线程池**:使用`ExecutorService`或`ThreadPoolExecutor`创建线程池可以更好地管理并发任务,确保资源的有效利用,并可以通过设置线程池小控制并发程度。 5. **线程同步**:如果需要多个线程同时读取但只修改一个线程的List,可以使用`synchronized`关键字对特定范围内的代码块进行同步,防止数据冲突。 6. **原子操作**:`AtomicInteger`, `AtomicLong`等原子类可以在多线程环境中保证操作的原子性,对于List的索引更新等场景很有帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值