多线程学习-day-05 ForkJoin概念及使用

线程基础、线程之间的共享和协作

(目前会将一些概念简单描述,一些重点的点会详细描述)

学习目标:多线程的并发工具类(1)

用途,概念:

        ForkJoinPool的优势在于,利用多核CPU,将一个任务,拆分成多个小任务 ,将这些小任务分配到多个处理器上并行执行;当小任务都执行完成之后,再将结果进行合并汇总。每个小任务间都没有关联,与原任务的形式相同。体现了“分而治之”的概念。任务递归分配成若干个小任务 -- 并行求值 -- 结果合并。

1、ForkJoinPool

        Java7提供了ForkJoinPool来支持将一个任务拆分成多个小任务进行并行计算,再将多个“小任务”的结果进行join汇总。

2、invoke、invokeAll

        执行指定的任务,等待任务,完成任务返回结果。

3、递归算法

        在继承RecurisiveTask(有返回结果),RecurisiveAction(无返回结果)类时,通过递归的方式,来将任务拆分成一个一个的小任务,通过invokeAll()方法来调度子任务,等待任务完成返回结果。注意,只有在ForkJoinPool执行计算过程中调用它们。

先来看一个例子:

        通过计算10000000组整型数的求和,用两种方式:普通单线程算法求和,利用ForkJoinPool多线程递归的求和。

定义一个随机产生10000000的整型数组的类

public class MakeArray {

	// 设置一个final类型的整型数,表示要操作的数量
	public static final int MAX_ARRAY = 1000000;

	// 定义一个方法,随机产生MAX_ARRAY个数
	public static int[] makeArray() {
		// 设置一个Random类,作为随机生产数作用
		Random rand = new Random();
		// 定义一个整型数组,能存储MAX_ARRAY个数
		int[] array = new int[MAX_ARRAY];
		for (int i = 0; i < MAX_ARRAY; i++) {
			// 往array数组里面随机添加一个数
			array[i] = rand.nextInt(MAX_ARRAY * 2);
		}
		// 返回
		return array;
	}
}

定义一个普通的单线程求和类

public class SumNormal {

	// 普通累加方法
	public static void main(String[] args) {
		// 定义时间
		long startTime = System.currentTimeMillis();
		// 定义一个数组,随机生成MAX_ARRAY个整型数
		int[] array = MakeArray.makeArray();
		// 定义一个sum变量进行累加统计
		int sum = 0;
		// 求和
		for (int i = 0; i < array.length; i++) {
			sum += array[i];
		}
		// 输出
		System.out.println(
				MakeArray.MAX_ARRAY + "个数,总和为sum = " + sum + "。共耗时:" + (System.currentTimeMillis() - startTime));
	}
}

控制台输出结果:
1000000个数,总和为sum = -1369059355。共耗时:28

定义多线程求和类

public class SumArray {

	private static class SumTask extends RecursiveTask<Integer> {
		private static final long serialVersionUID = 1L;
		
		// 定义一个阈值
		private static final int THERHOLD = MakeArray.MAX_ARRAY / 10;
		// 定义一个要操作的数组
		private int[] sum;
		// 定义起始下标
		private int start;
		// 定义结尾下标
		private int end;

		public SumTask(int[] sum, int start, int end) {
			this.sum = sum;
			this.start = start;
			this.end = end;
		}

		@Override
		protected Integer compute() {
			if (end - start < THERHOLD) {
				int count = 0;
				for (int i = start; i < end; i++) {
					count += sum[i];
				}
				return count;
			} else {
				int mid = (start + end) / 2;
				SumTask sumTaskA = new SumTask(sum, start, mid);
				SumTask sumTaskB = new SumTask(sum, mid + 1, end);
				// 将线程阻塞,必须所有任务执行完成之后,统一返回
				invokeAll(sumTaskA, sumTaskB);
				return sumTaskA.join() + sumTaskB.join();
			}
		}
	}

	public static void main(String[] args) {
		long startTime = System.currentTimeMillis();
		System.out.println("线程开始运行...");
		ForkJoinPool pool = new ForkJoinPool();
		int[] sum = MakeArray.makeArray();

		SumTask sumTask = new SumTask(sum, 0, sum.length - 1);
		pool.invoke(sumTask);
		System.out.println(MakeArray.MAX_ARRAY + "个数据,总和 sum = " + sumTask.join() + ", 共耗时:"
				+ (System.currentTimeMillis() - startTime));
	}
}

控制台输出结果:
线程开始运行...
1000000个数据,总和 sum = -941995665, 共耗时:35

我们发现:

单线程求和共耗时:28ms

多线程求和共耗时:35ms

这样可以给我们一种警示,并非引入多线程就一定能提升效率,而是要看实际情况,经过多轮测试选择合适的方式来进行操作。

 

可以引申出其他的场景:

统计某个系统下的文件夹数量、超大规模的一些操作,例如几百万,上千万的数据库等操作等等场景。大家都可以根据不同的场景进行多多测试。个人感觉未来这种多线程递归操作会用的很多的地方,希望通过学习拓展思路

来自享学IT教育课后总结。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值