目录
决方案:3.通过Stream中的toArray或collect操作
一、串行的Stream流
前面所使用的都是串行流,也就是在一个上面执行的
public class ParallelStreamTest01 {
public static void main(String[] args) {
//串行流
Stream.of(5,8,4,6,9)
.filter(s -> {
System.out.println(Thread.currentThread() + " "+s);
return s>1;
}).count();
}
}
输出:
Thread[main,5,main] 5
Thread[main,5,main] 8
Thread[main,5,main] 4
Thread[main,5,main] 6
Thread[main,5,main] 9
二、并行流
parallelStream就是一个并行执行的流,它通过默认的ForkjoinPool,可以提高多线程的任务执行速度
2.1 并行流的两种获取方式:
public class ParallelStreamTest02 {
public static void main(String[] args) {
//并行流的两种获取方式
List<Integer> list = new ArrayList<>();
Stream<Integer> integerStream = list.parallelStream();
Stream<Integer> parallel = Stream.of(5, 8, 4, 6, 9).parallel();
}
}
2.2 用法
public class ParallelStreamTest03 {
public static void main(String[] args) {
//并行流
Stream.of(5, 8, 4, 6, 9).parallel()
.filter(s -> {
System.out.println(Thread.currentThread() + " " + s);
return s > 2;
}).count();
}
}
输出:
Thread[main,5,main] 4
Thread[ForkJoinPool.commonPool-worker-2,5,main] 9
Thread[ForkJoinPool.commonPool-worker-3,5,main] 5
Thread[ForkJoinPool.commonPool-worker-4,5,main] 6
Thread[ForkJoinPool.commonPool-worker-1,5,main] 8
2.3 执行效率比较
public class ParallelStreamTest04 {
private static Long num = 2000000000L;
public static void main(String[] args) {
//串行流与并行流对比
//for循环
long l = System.currentTimeMillis();
long res = 0;
for (int i = 0; i < num; i++) {
res += i;
}
System.out.println("for循环消耗时间:" + String.valueOf(System.currentTimeMillis()-l));
//串行
long l2 = System.currentTimeMillis();
LongStream.rangeClosed(0,num)
.reduce(Long::sum);
System.out.println("串行消耗时间:" + String.valueOf(System.currentTimeMillis()-l2));
//并行
long l3 = System.currentTimeMillis();
LongStream.rangeClosed(0,num)
.parallel()
.reduce(0,Long::sum);
System.out.println("并行消耗时间:" + String.valueOf(System.currentTimeMillis()-l3));
}
}
执行结果:
for循环消耗时间:1201
串行消耗时间:2567
并行消耗时间:500
三、并行流中的线程安全问题
在多线程处理下,会出现数据安全问题:
public class ParallelStreamTest05 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(i);
}
System.out.println(list.size());
List<Integer> listNew = new ArrayList<>();
//使用并行流向集合中添加数据
list.parallelStream()
.forEach(s -> listNew.add(s));
System.out.println(listNew.size());
}
}
输出结果: 1000 923 或者: 1000 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 823 at java.util.ArrayList.add(ArrayList.java:459) at com.jdk.Parallel.ParallelStreamTest05.lambda$main$0(ParallelStreamTest05.java:23) at com.jdk.Parallel.ParallelStreamTest05$$Lambda$1/168423058.accept(Unknown Source) at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512) at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290) at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731) at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:902) at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1689) at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1644) at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
解决方案:1.加同步锁
public class ParallelStreamTest06 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(i);
}
System.out.println(list.size());
List<Integer> listNew = new ArrayList<>();
Object object = new Object();
//使用并行流向集合中添加数据.
//解决数据安全问题:1.加同步锁
list.parallelStream()
.forEach(s -> {
synchronized (object){
listNew.add(s);
}
});
System.out.println(listNew.size());
//另一种写法
List<Integer> list2 = new ArrayList<>();
Object object2 = new Object();
IntStream.range(0,1000)
.parallel()
.forEach(i -> {
synchronized (object2){
list2.add(i);
}
});
System.out.println(list2.size());
}
}
解决方案:2.使用线程安全的容器
public class ParallelStreamTest06 {
public static void main(String[] args) {
//使用线程安全的容器
List<Integer> list3 = new ArrayList<>();
List<Integer> list3New = Collections.synchronizedList(list3);
Object obj = new Object();
IntStream.range(0,1000)
.parallel().forEach(s -> list3New.add(s));
System.out.println(list3New.size());
}
}
决方案:3.通过Stream中的toArray或collect操作
public class ParallelStreamTest06 {
public static void main(String[] args) {
//通过Stream中的toArray或collect操作
List<Integer> list4 = new ArrayList<>();
List<Integer> collect = IntStream.range(0, 1000)
.parallel()
.boxed()
.collect(Collectors.toList());
System.out.println(collect.size());
}
}
四、Fork/Join框架
4.1三个模块
4.2 Fork/join原理-分治法
4.3Fork/join原理-工作窃取算法
4.3 Fork/join案例
需求:使用Fork/join计算1-10000的和,当一个任务计算数量大于3000的时候拆分任务,数量小于3000的时候就计算。
案例实现:
/**
* @author: 程序员Haris
* @description:
*/
public class ForkjoinTest01 {
public static void main(String[] args) {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
SumrecursiveTask sumrecursiveTask = new SumrecursiveTask(1,10000l);
Long result = forkJoinPool.invoke(sumrecursiveTask);
long end = System.currentTimeMillis();
System.out.println("result = " + result);
System.out.println("总耗时:" + (end -start));
}
}
class SumrecursiveTask extends RecursiveTask<Long>{
private static final long THRESHOLD = 3000l;
private final long start;
private final long end;
public SumrecursiveTask(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long length= end - start;
if(length <= THRESHOLD){
//任务不用拆分,可以计算
long sum=0;
for (long i = start; i < end; i++) {
sum +=i;
}
System.out.println("计算:" + start + "-->" + end +",的结果为:" + sum);
return sum;
}else{
//数量大于预定的数量,说明还需要拆分
long middle = (start + end) / 2;
System.out.println("拆分:左边" + start + "-->" +middle + ",右边" + (middle+1)+"-->" + end);
SumrecursiveTask left = new SumrecursiveTask(start, middle);
left.fork();
SumrecursiveTask right = new SumrecursiveTask(middle + 1, end);
right.fork();
return left.join()+ right.join();
}
}
}
输出:
拆分:左边1-->5000,右边5001-->10000
拆分:左边5001-->7500,右边7501-->10000
拆分:左边1-->2500,右边2501-->5000
计算:1-->2500,的结果为:3123750
计算:2501-->5000,的结果为:9371250
计算:5001-->7500,的结果为:15618750
计算:7501-->10000,的结果为:21866250
result = 49980000
总耗时:399