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 按自己的需求实现并行迭代器

相关文章
|
22天前
|
Java
Java中的抽象类:深入了解抽象类的概念和用法
Java中的抽象类是一种不能实例化的特殊类,常作为其他类的父类模板,定义子类行为和属性。抽象类包含抽象方法(无实现)和非抽象方法。定义抽象类用`abstract`关键字,子类继承并实现抽象方法。抽象类适用于定义通用模板、复用代码和强制子类实现特定方法。优点是提供抽象模板和代码复用,缺点是限制继承灵活性和增加类复杂性。与接口相比,抽象类可包含成员变量和单继承。使用时注意设计合理的抽象类结构,谨慎使用抽象方法,并遵循命名规范。抽象类是提高代码质量的重要工具。
34 1
|
2月前
|
前端开发 Java
java中的Queue队列的用法
java中的Queue队列的用法
19 1
|
2月前
|
XML Java 编译器
java aspectjrt AOP 用法
java aspectjrt AOP 用法
22 0
|
2月前
|
Java Spring 容器
【Java】Spring如何扫描自定义的注解?
【Java】Spring如何扫描自定义的注解?
36 0
|
2天前
|
Java API 开发者
Java8并行流——Spliterator
Java8并行流——Spliterator
9 1
|
11天前
|
JSON Java 数据格式
Java QueryWrapper基本用法
Java QueryWrapper基本用法
14 2
|
18天前
|
Java
Java配置大揭秘:读取自定义配置文件的绝佳指南
Java配置大揭秘:读取自定义配置文件的绝佳指南
17 0
Java配置大揭秘:读取自定义配置文件的绝佳指南
|
22天前
|
NoSQL Java Redis
Java自定义线程池的使用
Java自定义线程池的使用
|
2月前
|
Java
java 自定义注解 实现限流
java 自定义注解 实现限流
14 1