之前有同事在做多线程数据处理时用到了Spliterator ,对于Spliterator的
trySplit 不太了解,于是做了个demo ,简单对方法进行了说明,以此记录,首先来看一下官方对Spliterator 的方法描述,直接搬来jdk的介绍
|
characteristics() 返回此Spliterator及其元素的一组特征。 |
long |
estimateSize() 返回 |
default void |
forEachRemaining(Consumer<? super T> action) 在当前线程中依次执行每个剩余元素的给定操作,直到所有元素都被处理或动作引发异常。 |
default Comparator<? super T> |
getComparator() 如果这个Spliterator的来源是 |
default long |
getExactSizeIfKnown() 方便的方法返回 |
default boolean |
hasCharacteristics(int characteristics) 返回 |
boolean |
tryAdvance(Consumer<? super T> action) 如果剩下的元素存在,执行给定的操作,返回 |
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();
图形展示看起来容易些
我用的是intStream ,它返回的迭代器是
我们来看一下他的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 按自己的需求实现并行迭代器