JAVA8 Spliterator 并行迭代器用法以及 自定义Spliterator (一)

简介: JAVA8 Spliterator 并行迭代器用法以及 自定义Spliterator (一)

之前有同事在做多线程数据处理时用到了Spliterator ,对于Spliterator的


trySplit 不太了解,于是做了个demo ,简单对方法进行了说明,以此记录,首先来看一下官方对Spliterator 的方法描述,直接搬来jdk的介绍

int

characteristics()

返回此Spliterator及其元素的一组特征。

long estimateSize()

返回 forEachRemaining(java.util.function.Consumer<? super T>)遍历将遇到的元素数量的估计,如果无穷大,未知数或计算 成本太高,则返回 Long.MAX_VALUE 

default void forEachRemaining(Consumer<? super T> action)

在当前线程中依次执行每个剩余元素的给定操作,直到所有元素都被处理或动作引发异常。

default Comparator<? super T> getComparator()

如果这个Spliterator的来源是SORTEDComparator ,返回Comparator 

default long getExactSizeIfKnown()

方便的方法返回 estimateSize()如果这个Spliterator是 SIZED ,否则 -1 

default boolean hasCharacteristics(int characteristics)

返回 true如果Spliterator的 characteristics()包含所有给定的特性。

boolean tryAdvance(Consumer<? super T> action)

如果剩下的元素存在,执行给定的操作,返回true ; 否则返回false 

Spliterator<T> trySplit()

如果此分割器可以被分区,返回一个包含元素的Spliter,当从该方法返回时,它不会被该Spliter所覆盖。

直接看文档肯定时一知半解的,写了个小demo,demo主要功能就是我想用spliterator

对0~100 的数字进行切割,切割后放在四个线程中同时进行遍历,达到每个线程遍历25个数字的效果,我们看一下

class SpliteratorThread extends Thread {
        private Spliterator spliterator;
        private CountDownLatch countDownLatch;
        private String name;
        public SpliteratorThread(Spliterator spliterator, CountDownLatch countDownLatch, String name) {
            this.spliterator = spliterator;
            this.countDownLatch = countDownLatch;
            this.name = name;
        }
        @Override
        public void run() {
            Thread.currentThread().setName(this.name);
            spliterator.forEachRemaining(x -> {
                System.out.println(SpliteratorThread.currentThread().getName() + ":" + x);
            });
            countDownLatch.countDown();
        }
    }
 @Test
    public void streamSpliteratorTest() throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(4);
        IntStream intStream = IntStream.range(0, 100);
        //原始迭代器
        Spliterator originSp = intStream.spliterator();
        //sp1是原始迭代器进行一次分割后分割出来的迭代器
        Spliterator sp1 = originSp.trySplit();
        System.out.println("originSp剩余数量:" + originSp.estimateSize());
        // //sp2是原始迭代器进行二次分割后分割出来的迭代器
        Spliterator sp2 = originSp.trySplit();
        System.out.println("originSp剩余数量:" + originSp.estimateSize());
        //sp3是sp1进行一次分割后分割出来的迭代器
        Spliterator sp3 = sp1.trySplit();
        System.out.println("sp1剩余数量:" + sp1.estimateSize());
        System.out.println("sp3剩余数量:" + sp3.estimateSize());
        //我们启动线程进行并行迭代(模拟stream parallel模式)查看输出结果
        new SpliteratorThread(originSp, countDownLatch, "线程1").start();
        new SpliteratorThread(sp1, countDownLatch, "线程2").start();
        new SpliteratorThread(sp2, countDownLatch, "线程3").start();
        new SpliteratorThread(sp3, countDownLatch, "线程4").start();
        countDownLatch.await();
    }

来看一下输出

originSp剩余数量:50
originSp剩余数量:25
sp1剩余数量:25
线程4:0
线程4:1
线程4:2
线程4:3
线程4:4
线程4:5
线程4:6
线程4:7
线程4:8
线程4:9
线程4:10
线程4:11
线程4:12
线程4:13
线程4:14
线程4:15
线程4:16
线程4:17
线程4:18
线程4:19
线程4:20
线程4:21
线程4:22
线程4:23
线程4:24
线程1:75
线程2:25
线程2:26
线程2:27
线程2:28
线程2:29
线程2:30
线程2:31
线程2:32
线程2:33
线程2:34
线程2:35
线程2:36
线程2:37
线程2:38
线程2:39
线程2:40
线程2:41
线程2:42
线程2:43
线程2:44
线程2:45
线程2:46
线程2:47
线程2:48
线程2:49
线程3:50
线程3:51
线程3:52
线程3:53
线程3:54
线程3:55
线程3:56
线程3:57
线程3:58
线程3:59
线程3:60
线程3:61
线程3:62
线程3:63
线程1:76
线程1:77
线程1:78
线程1:79
线程1:80
线程1:81
线程1:82
线程1:83
线程1:84
线程1:85
线程1:86
线程1:87
线程1:88
线程1:89
线程1:90
线程1:91
线程1:92
线程1:93
线程1:94
线程1:95
线程1:96
线程1:97
线程1:98
线程1:99
线程3:64
线程3:65
线程3:66
线程3:67
线程3:68
线程3:69
线程3:70
线程3:71
线程3:72
线程3:73
线程3:74

从结果来看确实做到了横向切割,但是看起来代码是很奇怪的,我们需要这样切割才可以平均分割,这是默认的规则,如果希望按照自己数据线程以及规则切割可以实现自己的spliterator,这在后面会有说到

 Spliterator originSp = intStream.spliterator();
 Spliterator sp1 = originSp.trySplit();
 Spliterator sp2 = originSp.trySplit();
 Spliterator sp3 = sp1.trySplit();

图形展示看起来容易些

1.png

我用的是intStream ,它返回的迭代器是

1.png

1.png

我们来看一下他的trySplit 方法

@Override
        public Spliterator.OfLong trySplit() {
            long size = estimateSize();
            return size <= 1
                   ? null
                   // Left split always has a half-open range
                   : new RangeLongSpliterator(from, from = from + splitPoint(size), 0);
        }

其实他默认的分割方式就是对半切割分割法),其实大部分 Collection接口都实现了自己的spliterator


有了之前的小demo,有兴趣的可以看看下一篇《JAVA8 Spliterator 并行迭代器用法以及 自定义Spliterator (二) 》 自定义spliterator 按自己的需求实现并行迭代器

相关文章
|
3月前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
2548 2
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
3月前
|
Java
java实现从HDFS上下载文件及文件夹的功能,以流形式输出,便于用户自定义保存任何路径下
java实现从HDFS上下载文件及文件夹的功能,以流形式输出,便于用户自定义保存任何路径下
128 34
|
4月前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
129 1
|
4月前
|
Java
在 Java 中,如何自定义`NumberFormatException`异常
在Java中,自定义`NumberFormatException`异常可以通过继承`IllegalArgumentException`类并重写其构造方法来实现。自定义异常类可以添加额外的错误信息或行为,以便更精确地处理特定的数字格式转换错误。
68 1
|
4月前
|
Java 编译器 API
如何在 Java 中避免使用迭代器
在Java中,为了避免使用迭代器,可以采用foreach循环来遍历集合或数组,简化代码,提高可读性。此外,Java 8引入的Stream API提供了更强大的功能,如filter、map等方法,能够以函数式编程风格处理数据,进一步减少对传统迭代器的依赖。
91 7
|
4月前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
4月前
|
Java 开发者 Spring
[Java]自定义注解
本文介绍了Java中的四个元注解(@Target、@Retention、@Documented、@Inherited)及其使用方法,并详细讲解了自定义注解的定义和使用细节。文章还提到了Spring框架中的@AliasFor注解,通过示例帮助读者更好地理解和应用这些注解。文中强调了注解的生命周期、继承性和文档化特性,适合初学者和进阶开发者参考。
129 14
|
5月前
|
存储 安全 Java
深入理解Java中的FutureTask:用法和原理
【10月更文挑战第28天】`FutureTask` 是 Java 中 `java.util.concurrent` 包下的一个类,实现了 `RunnableFuture` 接口,支持异步计算和结果获取。它可以作为 `Runnable` 被线程执行,同时通过 `Future` 接口获取计算结果。`FutureTask` 可以基于 `Callable` 或 `Runnable` 创建,常用于多线程环境中执行耗时任务,避免阻塞主线程。任务结果可通过 `get` 方法获取,支持阻塞和非阻塞方式。内部使用 AQS 实现同步机制,确保线程安全。
340 3
|
5月前
|
安全 Java
如何在 Java 中创建自定义安全管理器
在Java中创建自定义安全管理器需要继承SecurityManager类并重写其方法,以实现特定的安全策略。通过设置系统安全属性来启用自定义安全管理器,从而控制应用程序的访问权限和安全行为。
138 1
|
5月前
|
Java
让星星⭐月亮告诉你,自定义定时器和Java自带原生定时器
定时器是一种可以设置多个具有不同执行时间和间隔的任务的工具。本文介绍了定时器的基本概念、如何自定义实现一个定时器,以及Java原生定时器的使用方法,包括定义定时任务接口、实现任务、定义任务处理线程和使用Java的`Timer`与`TimerTask`类来管理和执行定时任务。
133 3

热门文章

最新文章