【多线程开发 1】从多线程读取文件开始实践多线程开发

本文介绍了如何使用Java的多线程和线程池技术(如ThreadPoolExecutor)读取文件中的单词,统计每个单词出现的次数,并探讨了使用ConcurrentHashMap与Future在不阻塞主线程的情况下的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2024年5月3日

今天心情不错,写一篇多线程开发的文章过过瘾。 话不多说,问题如下

利用多线程读取文件中的所有单词,统计每个单词出现的次数。

一看就知道问题很简单,话不多说代码如下,其中的很多代码我们都会一个一个的解析

    public static void main(String[] args) throws FileNotFoundException {
        ThreadPoolExecutor executorService = new ThreadPoolExecutor(
                20,  // 核心线程数量
                20,              //最大线程数
                60,             //空闲临时线程最大存活时间(数值)
                TimeUnit.SECONDS,//空闲临时线程最大存活时间(单位)
                new ArrayBlockingQueue<>(1_000_000_00),//任务队列,也就是一个堵塞队列,也可以使用LinkedBlockingQueue这个阻塞队列
                Executors.defaultThreadFactory(),//用线程池工具类Executors创建线程的工厂
                new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略中其中一个,丢弃任务并抛出RejectedExecutionException
        );Map<String, Integer> hashMap = new HashMap<>();
        File file = new File("C:\Users\Desktop\the_text_book.txt");System.out.println(executorService.getActiveCount());try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) {
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                String[] s = line.split(" ");
                if (s.length == 0) {
                    continue;
                }
                executorService.submit(() -> {
                    List<String> results = Arrays.asList(s);
                    results.forEach(x -> {
                        if (!(Objects.equals(x, "") || x == null)) {
                            if (hashMap.containsKey(x)) {
                                hashMap.put(x, hashMap.get(x) + 1);
                            } else {
                                hashMap.put(x, 1);
                            }
                        }
                    });
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }while (true) {
            if (executorService.getActiveCount() == 0) {
                hashMap.forEach((k, v) -> {
                    System.out.println(k + " " + v);
                });
                executorService.shutdown();
                break;
            }
        }
    }

创建线程池

ThreadPoolExecutor executorService = new ThreadPoolExecutor(
                20,  // 核心线程数量
                20,              //最大线程数
                60,             //空闲临时线程最大存活时间(数值)
                TimeUnit.SECONDS,//空闲临时线程最大存活时间(单位)
                new ArrayBlockingQueue<>(1_000_000_00),//任务队列,也就是一个堵塞队列,也可以使用LinkedBlockingQueue这个阻塞队列
                Executors.defaultThreadFactory(),//用线程池工具类Executors创建线程的工厂
                new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略中其中一个,丢弃任务并抛出RejectedExecutionException
        );

如果你已经是一名稍微有点年头的开发者,你肯定知道怎么写创建线程池的。说句题外话,如果是使用的把配置信息交给框架自动配置的开发者,可以将线程池也放到组件容器中,比如Spring框架就可以这么用。

获取输出结果

        while (true) {
            if (executorService.getActiveCount() == 0) {
                hashMap.forEach((k, v) -> {
                    System.out.println(k + " " + v);
                });
                executorService.shutdown();
                break;
            }
        }

因为使用了线程池,所以如果需要查看最后的效果,我可能会这么写:最后写一个while循环阻塞掉主线程,知道拿到对应结果(使用的是自定义线程池的API)

如果不想阻塞主线程,可以尝试使用别的方式,比如说使用Java8的API,CompletableFuture类进行实现,这个有一个方式是*allOf() *,该方法可以做到让全部的CompletableFuture任务都完成后再进行后续输出,笔者认为这个是通过创建一个异步线程/子线程来专门用于某一行代码的实现,就可以不使用阻塞线程的代码了,实现的代码如下:

                // 创建CompletableFuture,并将任务提交给线程池
                futureList.add(CompletableFuture.supplyAsync(() -> {
                    List<String> results = Arrays.asList(s);
                    results.forEach(x -> {
                        if (!(Objects.equals(x, "") || x == null)) {
                            if (hashMap.containsKey(x)) {
                                hashMap.put(x, hashMap.get(x) + 1);
                            } else {
                                hashMap.put(x, 1);
                            }
                        }
                    });
                    return null;
                }, executorService));
            }
            CompletableFuture.allOf(futureList.toArray(futureList.toArray(new CompletableFuture[0]))).whenComplete((v, th) -> {
                hashMap.forEach((k, value) -> {
                    System.out.println(k + " " + value);
                });
            }).join();

这样就可以在不阻塞主线程的情况下实现效果了

不过有一件事我也不是很明白,交给阅读这篇文章的你,我使用了ConcurrentHashMap和不使用ConcurrentHashMap的情况下,效果一样没有问题,在写这篇文章的时候我也一直没搞明白<3;

希望能对你有所帮助;

最终代码

偶对了,为了方便大家直接使用我就把代码直接完全贴一份到博客里面吧

public static void main(String[] args) throws FileNotFoundException {
        ThreadPoolExecutor executorService = new ThreadPoolExecutor(
                20,  // 核心线程数量
                20,              //最大线程数
                60,             //空闲临时线程最大存活时间(数值)
                TimeUnit.SECONDS,//空闲临时线程最大存活时间(单位)
                new ArrayBlockingQueue<>(1_000_000_00),//任务队列,也就是一个堵塞队列,也可以使用LinkedBlockingQueue这个阻塞队列
                Executors.defaultThreadFactory(),//用线程池工具类Executors创建线程的工厂
                new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略中其中一个,丢弃任务并抛出RejectedExecutionException
        );Map<String, Integer> hashMap = new ConcurrentHashMap<>();
        File file = new File("C:\Users\Desktop\the_text_book.txt");System.out.println(executorService.getActiveCount());List<CompletableFuture> futureList = new ArrayList<>();
        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) {
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                String[] s = line.split(" ");
                if (s.length == 0) {
                    continue;
                }
                // 创建CompletableFuture,并将任务提交给线程池
                futureList.add(CompletableFuture.supplyAsync(() -> {
                    List<String> results = Arrays.asList(s);
                    results.forEach(x -> {
                        if (!(Objects.equals(x, "") || x == null)) {
                            if (hashMap.containsKey(x)) {
                                hashMap.put(x, hashMap.get(x) + 1);
                            } else {
                                hashMap.put(x, 1);
                            }
                        }
                    });
                    return null;
                }, executorService));
            }
            CompletableFuture.allOf(futureList.toArray(futureList.toArray(new CompletableFuture[0]))).whenComplete((v, th) -> {
                hashMap.forEach((k, value) -> {
                    System.out.println(k + " " + value);
                });
            }).join();
        } catch (Exception e) {
            e.printStackTrace();
        }}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艾迪的技术之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值