ForkJoinPool forkJoinPool = new ForkJoinPool(5);
//需要下单并成功创建的计划
List<String> sourceList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
sourceList.add("S-erqe");
sourceList.add("S-43432");
sourceList.add("S-5234");
}
for (int i = 0; i < 100; i++) {
sourceList.add("H-353452");
sourceList.add("H-353452");
sourceList.add("H-353452");
}
//需要下单并成功创建的计划
List<String> startWithSList = new ArrayList<>();
//没有需要下订单的计划
List<String> startWithHList = new ArrayList<>();
//没有需要下订单的计划
List<String> result = new CopyOnWriteArrayList<>();
result = forkJoinPool.submit(() -> sourceList.parallelStream().map(e -> {
if (e.startsWith("S")) {
startWithSList.add(e);
}
if (e.startsWith("H")) {
startWithHList.add(e);
}
return e.split("-")[1];
}).collect(Collectors.toList())).join();
System.out.println("以S开头的集合有:");
startWithSList.forEach(s -> {
if (s == null) {
System.out.println(s);
}
}
);
System.out.println("以H开头的集合有:");
startWithHList.forEach(s -> {
if (s == null) {
System.out.println(s);
}
}
);
System.out.println("去除开头的所有集合:");
result.forEach(s -> {
System.out.print(s+",");
}
);
}
运行结果一:在多线程中使用 ArrayList 来分别收集以S,和H 开头的 字符串。 看打印的结果发现出现 null ;
以S开头的集合有:
null
null
以H开头的集合有:
null
去除开头的所有集合:
erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,erqe,43432,5234,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452,353452
运行结果二: 出现了数组越界问题;
原因分析: ArrayList 是属于线程不安全的类;在多线程运行过程中出现了对应的线程安全问题。
解决方式:使用线程安全的对应的类,如:CopyOnWriteArrayList 代替ArrayList.
或者是 Collections.synchronizedList(startWithHList)
至此,就可以看到不会再出现上面的空指针和数组越界的问题了。
ArrayList 为什么会是线程不安全的呢?
首先查看一下,ArrayList 的add方法实现:我们知道 size++ 是非原子性操作的。
为什么会出现null 的情况呢?
当线程A 和线程B 同时执行 size++ 的操作时,假设 size = 3;A和B 都是 elementData [3++] = e; 此时 element[3] 可能为A线程的e,也可能时B线程的e。而此时 e 可能被加了2次 e = 5 ;这是 element[4] 就没有被赋值,此时就出现了 null的情况。
从新认识一下ArrayList 的数据结构:
1,底层的数据存储是放在一个数组里的[]. 这里涉及到一个数组长度的初始化大小问题: 默认大小为10; 通常使用的默认无参构造方法,实现即为 this.elementData ={};此时的size = 0;
2,新增方法;
2.1 先对初始化的数组进行扩容。如果是首次添加数据,则默认初始化为 10;此时会调用一次扩容算法。新的数组长度会是原长度的1.5倍;确定好数组需要扩容的大小后,开始复制数组到新数组中;
中间插入一个小疑问:
2.2 初始化/扩容好对应的数组大小后,就可以开始添加新的数据了。
到这里就知道为什么会出现数组越界的情况了。
当新增的时候,会首先确认是否需要扩容。此时A.B 线程同时执行了add()方法,都已经确定了此时的数组的容量大小。因为是线程共享的size .此时 size++ 可能会大于 初始化好的数组容量导致 在执行elementData[size++]的时候 已经超过了数组大小,此时就会报数组越界异常;
再看看线程安全的类是如何解决这个问题的呢?
CopyOnWriteArrayList加锁的方式,保证线程安全。
Collections.synchronizedList(startWithHList) 在线程不安全的ArrayList .add()上加了一把锁。从而保证了线程安全。
结束语:虽然都知道ArrayList ,HashMap 是线程不安全的类,但是写代码的时候还是哐哐就给搞上去了。要不是这次生产出现了一个偶发的bug ,也不会有如此深刻的印象,还是要学以致用。