Java8函数式编程之并行执行和串行执行

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013560667/article/details/89186534

今天我们来整理一下java8的知识点,java8最主要的知识点是什么呢?只要大家关注技术方向,大家一定知道是lambda,对了但是他是一类特殊的函数式接口,今天我们这里先不做函数式接口的概念的整理,因为这里的知识点比较多,也不是很难,上网搜搜资料就应该很清楚了,我们今天要说的是一些比较不好理解或者不好找资料的知识点,今天我们来说说Stream的并行和串行。Stream中有两个函数parallel()和sequential(),分别代表了并行和串行,串行比较好理解,就是在主线程上按顺序执行逻辑代码,那么并行呢?那么我们要来说说什么是并行和并发,以前我们的CPU是单核的,多个任务是通过划分时间片轮训来执行任务,这样的逻辑叫做并发。现代的CPU的多核的,有多少个核心就可以同一时间可以运行更多的任务,所以并行和并发大家就明白了。好了概念说完了,我们说说具体的,我们默认的Stream是不使用并行的,就是默认是在主线程上执行当前的任务,这样的话其实并没有起到非阻塞的作用,假如我们的业务是非常耗的时候,这会就会阻塞主线程执行,导致任务堆积,系统性能下降,所以为了实现主线程的非阻塞,我们以前的实现方式是直接实现一个线程池,然后将任务丢给线程池,我们现在呢,lambda提供了内置的线程任务分解,就是parallel(),parallel方法通过ForkJoinPool来实现的线程池,默认线程数为

Runtime.getRuntime().availableProcessors()-1

可能有些人会问了,为什么是可用线程数减一呢?因为最优的策略是每个CPU处理器分配一个线程,然而主线程也算一个线程,所以要占一个名额。所以分配的线程比可用线程少一个。我们可以通过代码

ForkJoinPool.getCommonPoolParallelism()

来查看在parallel()中可以使用的线程数量,我们可以通过配置系统环境参数来设置线程数

System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "20");

注意java.util.concurrent.ForkJoinPool.common.parallelism是final类型的,整个JVM中只允许设置一次。上面的这种线程模型适合CPU密集计算型的任务,但是我们实际当中可能大多数业务都是IO密集型的业务,可能我们会需要更多的线程来处理IO业务,而不是少量线程的计算任务。所以我们需要自定义线程数量。前面我们说了parallel()函数使用的是基于ForkJoinPool来实现的,所以我们只需要定义一个ForkJoinPool线程池来执行对应的任务即可,下面我分别整理了同步和异步的整理方式,

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.IntStream;

public class Test7 {

	public static void main(String[] args) {
		ForkJoinPool forkJoinPool = new ForkJoinPool(5);
		try {
			for(int i=0;i<10;i++) {
				long count = forkJoinPool.submit(() -> {
					System.out.println("thread:"+Thread.currentThread().getId());
					return IntStream.range(0, 10000).parallel().filter(item->item>999).count();
				}).get();
				System.out.println(count);
			}
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}

		System.out.println(Runtime.getRuntime().availableProcessors() + "-" + ForkJoinPool.getCommonPoolParallelism());
	}

}
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class Test8 {

	public static void main(String[] args) {
		List<Integer> list = new ArrayList<Integer>();
		list.add(19);
		list.add(18);
		list.add(17);

		ForkJoinPool forkJoinPool = new ForkJoinPool(2);
		CompletableFuture<List<String>> futureResult = CompletableFuture.supplyAsync(
				() -> list.stream().parallel().map(item -> String.valueOf(item)).collect(Collectors.toList()), forkJoinPool);
		futureResult.whenComplete((r, e) -> System.out.println(r));
		
		try {
			TimeUnit.SECONDS.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

}

通过上面的整理,已经可以理解并行执行的原理和逻辑了,下面我们在来说说下一个函数sequential(),这个函数的作用是将并行流改为串行流,具体一个lambda是并行流还是串行流,看的是最后一个函数式并行还是串行,且这两个函数都是可以多次调用的。因为我们的lambda表达式的组成是由,源-中间步骤-中间步骤-终点步骤来完成的,但我们连续定义中间步骤的时候,实际上我们的操作并没有真正触发,只有最后的终点步骤执行后,我们的任务才真正开始执行。好了,基于java8的并行和串行都整理完了。

猜你喜欢

转载自blog.csdn.net/u013560667/article/details/89186534
今日推荐