今天读了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的modCount
   private 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=0
            if ((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=mid
        return (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+1
             index = 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 loop
       ArrayList<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;
           }
           else
               mc = 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() {
        //打上特征值:、可以返回size
        return 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遍历
	 */
	@Test
	public 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")
		@Override
		public void run() {
			final String threadName = Thread.currentThread().getName();
			System.out.println("线程" + threadName + "开始运行-----");
			spliterator.trySplit().forEachRemaining(new Consumer() {
				@Override
				public 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();
	}
}

运行结果为:

今天读了JDK1.8源码,知道了并行迭代器Spliterator

从结果可以看出,四个线程遍历没有产生并发问题,

本文参考:https://www.cnblogs.com/nevermorewang/p/9368431.html        谢谢~

相关文章:

  • 2022-12-23
  • 2021-10-27
  • 2021-05-30
  • 2021-11-05
  • 2021-11-26
  • 2022-01-17
  • 2021-10-08
  • 2021-09-30
猜你喜欢
  • 2021-05-04
  • 2021-04-12
  • 2022-12-23
  • 2022-12-23
  • 2022-01-19
  • 2021-08-03
相关资源
相似解决方案