今天读了JDK1.8源码,知道了并行迭代器Spliterator
在JDK1.8的ArrayList里面偶然看到了這個內部類,同時對比了1.7的版本,發現1.7并沒有這后面的東西, 隨著好奇心,就搜了一下下,發現很有意思~? 也查了一些資料,如下總結:
Spliterator是什么?
Spliterator是一個可分割迭代器(splitable iterator),可以和iterator順序遍歷迭代器一起看。jdk1.8發布后,對于并行處理的能力大大增強,Spliterator就是為了并行遍歷元素而設計的一個迭代器,jdk1.8中的集合框架中的數據結構都默認實現了spliterator,后面我們也會結合ArrayList中的spliterator()一起解析。
Spliterator內部結構
//單個對元素執行給定的動作,如果有剩下元素未處理返回true,否則返回false boolean tryAdvance(Consumer<? super T> action);//對每個剩余元素執行給定的動作,依次處理,直到所有元素已被處理或被異常終止。默認方法調用tryAdvance方法 default void forEachRemaining(Consumer<? super T> action) {do { } while (tryAdvance(action)); }//對任務分割,返回一個新的Spliterator迭代器 Spliterator<T> trySplit();//用于估算還剩下多少個元素需要遍歷 long estimateSize();//當迭代器擁有SIZED特征時,返回剩余元素個數;否則返回-1 default long getExactSizeIfKnown() {return (characteristics() & SIZED) == 0 ? -1L : estimateSize(); }//返回當前對象有哪些特征值 int characteristics();//是否具有當前特征值 default boolean hasCharacteristics(int characteristics) {return (characteristics() & characteristics) == characteristics; } //如果Spliterator的list是通過Comparator排序的,則返回Comparator //如果Spliterator的list是自然排序的 ,則返回null //其他情況下拋錯 default Comparator<? super T> getComparator() {throw new IllegalStateException(); }JDK8源碼內的ArrayList中的ArrayListSpliterator
static final class ArrayListSpliterator<E> implements Spliterator<E> {//用于存放ArrayList對象private final ArrayList<E> list;//起始位置(包含),advance/split操作時會修改private int index;//結束位置(不包含),-1 表示到最后一個元素private int fence;//用于存放list的modCountprivate int expectedModCount;ArrayListSpliterator(ArrayList<E> list, int origin, int fence,int expectedModCount) {this.list = list;this.index = origin;this.fence = fence;this.expectedModCount = expectedModCount;}//獲取結束位置(存在意義:首次初始化石需對fence和expectedModCount進行賦值)private int getFence() {int hi;ArrayList<E> lst;//fence<0時(第一次初始化時,fence才會小于0):if ((hi = fence) < 0) {//list 為 null時,fence=0if ((lst = list) == null)hi = fence = 0;else {//否則,fence = list的長度。expectedModCount = lst.modCount;hi = fence = lst.size;}}return hi;}//分割list,返回一個新分割出的spliterator實例public ArrayListSpliterator<E> trySplit() {//hi為當前的結束位置//lo 為起始位置//計算中間的位置int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;//當lo>=mid,表示不能在分割,返回null//當lo<mid時,可分割,切割(lo,mid)出去,同時更新index=midreturn (lo >= mid) ? null :new ArrayListSpliterator<E>(list, lo, index = mid, expectedModCount);}//返回true 時,只表示可能還有元素未處理//返回false 時,沒有剩余元素處理了。。。public boolean tryAdvance(Consumer<? super E> action) {if (action == null)throw new NullPointerException();//hi為當前的結束位置//i 為起始位置int hi = getFence(), i = index;//還有剩余元素未處理時if (i < hi) {//處理i位置,index+1index = i + 1;@SuppressWarnings("unchecked") E e = (E)list.elementData[i];action.accept(e);//遍歷時,結構發生變更,拋錯if (list.modCount != expectedModCount)throw new ConcurrentModificationException();return true;}return false;}//順序遍歷處理所有剩下的元素public void forEachRemaining(Consumer<? super E> action) {int i, hi, mc; // hoist accesses and checks from loopArrayList<E> lst; Object[] a;if (action == null)throw new NullPointerException();if ((lst = list) != null && (a = lst.elementData) != null) {//當fence<0時,表示fence和expectedModCount未初始化,可以思考一下這里能否直接調用getFence(),嘿嘿?if ((hi = fence) < 0) {mc = lst.modCount;hi = lst.size;}elsemc = expectedModCount;if ((i = index) >= 0 && (index = hi) <= a.length) {for (; i < hi; ++i) {@SuppressWarnings("unchecked") E e = (E) a[i];//調用action.accept處理元素action.accept(e);}//遍歷時發生結構變更時拋出異常if (lst.modCount == mc)return;}}throw new ConcurrentModificationException();}public long estimateSize() {return (long) (getFence() - index);}public int characteristics() {//打上特征值:、可以返回sizereturn Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;} }以上為源碼講解部分,大意是說,這個就是用來多線程并行迭代的迭代器,這個迭代器的主要作用就是把集合分成了好幾段,每個線程執行一段,因此是線程安全的。基于這個原理,以及modCount的快速失敗機制,如果迭代過程中集合元素被修改,會拋出異常。
我們設計一個測試用例:創建一個長度為100的list,如果下標能被10整除,則該位置數值跟下標相同,否則值為aaaa。然后多線程遍歷list,取出list中的數值(字符串aaaa不要)進行累加求和。
測試代碼如下:
package com.turingschool.demo.ds;import java.util.ArrayList; import java.util.List; import java.util.Spliterator; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.regex.Pattern;import org.junit.Test;public class Atest {AtomicInteger count = new AtomicInteger(0);List<String> strList = createList();Spliterator spliterator = strList.spliterator();/*** 多線程計算list中數值的和 測試spliterator遍歷*/@Testpublic void mytest() {for (int i = 0; i < 4; i++) {new MyThread().start();}try {Thread.sleep(15000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("結果為:" + count);}class MyThread extends Thread {@SuppressWarnings("unchecked")@Overridepublic void run() {final String threadName = Thread.currentThread().getName();System.out.println("線程" + threadName + "開始運行-----");spliterator.trySplit().forEachRemaining(new Consumer() {@Overridepublic void accept(Object o) {if (isInteger((String) o)) {int num = Integer.parseInt(o + "");count.addAndGet(num);System.out.println("數值:" + num + "------" + threadName);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}});System.out.println("線程" + threadName + "運行結束-----");}}private List<String> createList() {List<String> result = new ArrayList<>();for (int i = 0; i < 100; i++) {if (i % 10 == 0) {result.add(i + "");} else {result.add("aaa");}}return result;}public static boolean isInteger(String str) {Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");return pattern.matcher(str).matches();} }運行結果為:
從結果可以看出,四個線程遍歷沒有產生并發問題,
本文參考:https://www.cnblogs.com/nevermorewang/p/9368431.html??????? 謝謝~
總結
以上是生活随笔為你收集整理的今天读了JDK1.8源码,知道了并行迭代器Spliterator的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: enum操作--获取枚举里的最大值
- 下一篇: 气象数据网址