多线程之Barrier栅栏获取多个任务结果并进行汇总

利用直接在一个线程中求和是效率非常低的,我们通过栅栏,先将这个问题拆分成一系列相互独立的子问题,通过栅栏后,将子问题的问题汇集起来,进行全部的求解。

CyclicBarrier是一个很好的实现,它的构造方法中有一个Runnable参数,这个是最后进行汇总的方法,比如我们最后将计算结果求和,这里就是求和的执行。

await()方法利用栅栏特性,等待所有线程求完元素之和再计算平均值。

书上具体的介绍:
CyclicBarrier可以使一定数量的参与方反复地在栅栏位置汇集,它在并行迭代算法中非常有用:这种算法通常将一个问题拆分成一系列相互独立的子问题。当线程到达栅栏位置时将调用await方法,这个方法将阻塞直到所有线程都到达栅栏位置。如果所有线程都到达了栅栏 ,那么栅栏将打开,此时所有线程都被释放,而栅栏将被重置以便下次使用。如果对await的调用超时,后者await阻塞的线程被中断,那么栅栏就被认为是打破了,所有阻塞的await调用都将终止并抛出BrokenBarrirerException。如果成功地通过栅栏,那么await将为每个线程返回一个唯一的到达索引号,我们可以利用这些索引来“选举”产生一个领导线程,并在下一次迭代中由该领导线程执行一些特殊的工作。CyclicBarrier还可以使你将一个栅栏操作传递给构造函数,这是一个Runnable,当成功通过栅栏会(在一个子任务线程中)执行它,但在阻塞线程被释放之前是不能执行的。

以下是我写的一个代码小例子:

package com.cc.mutilineExample.callableAndFuture.test4_CyclicBarrier;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CyclicBarrier;


/**
 * @ClassName: FutureResponseTest 
 * @Description: 根据交易请求的额度和方式,进行扣款汇总计算
 * 计算交易一共扣款的数额:
 * 		利用直接在一个线程中求和是效率非常低的,我们通过栅栏,先将这个问题拆分成一系列相互独立的子问题,通过
 * 栅栏后,将子问题的问题汇集起来,进行全部的求解。
 * 
 * 差不多,但是将最后汇总也包含进去了框架
 * 
 * 同样的,还是有些繁琐 
 * @author CC  
 * @date 2018年12月6日 上午10:44:20 
 * @version V1.0 
 */
public class CyclicBarrierTest {
	private final static int SIZE = 20;//交易请求数
	
	private final Double[] doubleArr = new Double[SIZE];//结果存储集
	
	private final CyclicBarrier barrier;//栅栏
	private final Worker[] workers;//子任务线程
	
	private final List requestList;//交易请求集
	
	//此方式的消耗时间不能直接写在调用那里了,异步了,计算不准确,放在这里
	private final long startTime = System.currentTimeMillis();//开始时间
	private static long endTime ;//结束时间
	//初始化
	public CyclicBarrierTest(List requestList) {
		barrier = new CyclicBarrier(SIZE, new Runnable() {
			/*
			 * (非 Javadoc) 
			 * 

Title: run

*

Description: 求和 * 当成功通过栅栏会(在一个子任务线程中,即只会进行一次)执行它,但在阻塞线程被释放之前是不能执行的。

* @see java.lang.Runnable#run() */ @Override public void run() { try { BigDecimal sum = BigDecimal.ZERO;//同理 double计算结果不精确 //方法get具有“状态依赖”的内在特性,因而调用者不需要知道任务的状态,此外在任务提交和获得 //结果中包含的安全发布属性也确保了这个方法是线程安全的。 //获得结果 for (int i = 0; i < doubleArr.length; i++) { //提交任务请求 double payMent = doubleArr[i]; sum = sum.add(new BigDecimal(String.valueOf(payMent))); } System.out.println("一共扣款了多少钱?" + sum.doubleValue()); endTime = System.currentTimeMillis(); System.out.println("消耗时间:" + (endTime - startTime) + "毫秒!"); } catch (Exception e) { e.printStackTrace(); } } }); this.requestList = requestList;//初始化交易请求,方便子任务线程使用 this.workers = new Worker[SIZE];//初始化子任务线程,创建线程 for (int i = 0; i < SIZE; i++) { workers[i] = new Worker(i); } } //子任务线程--即调用第三方服务 private class Worker implements Runnable{ private int index;//每个线程带自己的请求 public Worker(int index) { this.index = index; } /* (非 Javadoc) *

Title: run

*

Description: 调用第三方服务

* @see java.lang.Runnable#run() */ @Override public void run() { try { //根据index获取request,调用第三方服务 double result = CyclicBarrierTest.requestForService(requestList.get(index)); //结果存储在数组中 doubleArr[index] = result; } catch (Exception e) { e.printStackTrace(); } try { barrier.await();//利用栅栏特性,等待所有线程求完元素之和再计算平均值 } catch (Exception e) { return; } } } //开始执行--程序入口 public void start() { for (int i = 0; i < workers.length; i++) { new Thread(workers[i]).start();; } } //模拟第三方服务 public static double requestForService(Request request) throws InterruptedException, Exception{ if(null == request) { throw new Exception("请求为空!"); } if(request.getParam() <= 0) { throw new Exception("参数小于0,无法进行扣款!" + request); } System.out.println("开始处理请求..."); //为了简便直接返回一个结果即可 double result = 0.0; if("WeiXin".equals(request.getMethod())) { System.out.println("微信支付扣3%"); // result = request.getParam() * 0.03;//double类型计算结果不准确 例如17 * 0.05 返回 扣款数 0.8500000000000001 result = new BigDecimal(String.valueOf(request.getParam())).multiply(new BigDecimal("0.03")).doubleValue(); }else { System.out.println("其他支付直接扣5%"); result = new BigDecimal(String.valueOf(request.getParam())).multiply(new BigDecimal("0.05")).doubleValue(); } //模拟-使消耗时间长一些 Thread.sleep(3000); System.out.println(request + " 返回扣款结果:" + result); return result; } //调度请求,获得返回结果,并进行汇总处理 public static void main(String[] args) throws Exception { //模拟随机请求 final String[] methodStr = new String[] {"WeiXin","ZhiFuBao","WangYin"}; final String[] serviceStr = new String[] {"TaoBao","JingDong","TianMao"}; //为了方便,我们将请求先初始化完毕 final List requestList = new ArrayList(); for (int i = 0; i < 20; i++) { Request request = new Request(); request.setMethod(methodStr[(int) (Math.random() * 3)]); request.setParam((int) (Math.random() * 300)); request.setServieName(serviceStr[(int) (Math.random() * 3)]); requestList.add(request); } //累积计算所有请求的总扣款数--计算任务提前开始且每个都是分开的不相互影响 CyclicBarrierTest test = new CyclicBarrierTest(requestList); test.start(); } }

运行结果:
开始处理请求…
其他支付直接扣5%
开始处理请求…
其他支付直接扣5%
开始处理请求…
其他支付直接扣5%
开始处理请求…
其他支付直接扣5%
开始处理请求…
其他支付直接扣5%
开始处理请求…
开始处理请求…
微信支付扣3%
开始处理请求…
微信支付扣3%
开始处理请求…
微信支付扣3%
开始处理请求…
其他支付直接扣5%
开始处理请求…
微信支付扣3%
开始处理请求…
微信支付扣3%
开始处理请求…
开始处理请求…
其他支付直接扣5%
微信支付扣3%
微信支付扣3%
开始处理请求…
其他支付直接扣5%
开始处理请求…
其他支付直接扣5%
开始处理请求…
其他支付直接扣5%
开始处理请求…
其他支付直接扣5%
开始处理请求…
其他支付直接扣5%
开始处理请求…
其他支付直接扣5%
Request [method=WangYin, servieName=TaoBao, param=43] 返回扣款结果:2.15
Request [method=ZhiFuBao, servieName=TaoBao, param=179] 返回扣款结果:8.95
Request [method=WeiXin, servieName=JingDong, param=90] 返回扣款结果:2.7
Request [method=WeiXin, servieName=TianMao, param=76] 返回扣款结果:2.28
Request [method=WeiXin, servieName=JingDong, param=212] 返回扣款结果:6.36
Request [method=ZhiFuBao, servieName=TianMao, param=11] 返回扣款结果:0.55
Request [method=ZhiFuBao, servieName=TianMao, param=2] 返回扣款结果:0.1
Request [method=WangYin, servieName=TaoBao, param=291] 返回扣款结果:14.55
Request [method=WeiXin, servieName=JingDong, param=149] 返回扣款结果:4.47
Request [method=ZhiFuBao, servieName=TaoBao, param=276] 返回扣款结果:13.8
Request [method=WangYin, servieName=JingDong, param=228] 返回扣款结果:11.4
Request [method=WangYin, servieName=JingDong, param=293] 返回扣款结果:14.65
Request [method=WangYin, servieName=JingDong, param=127] 返回扣款结果:6.35
Request [method=ZhiFuBao, servieName=TaoBao, param=2] 返回扣款结果:0.1
Request [method=WangYin, servieName=TaoBao, param=284] 返回扣款结果:14.2
Request [method=WeiXin, servieName=TianMao, param=58] 返回扣款结果:1.74
Request [method=WeiXin, servieName=TaoBao, param=194] 返回扣款结果:5.82
Request [method=WeiXin, servieName=TaoBao, param=180] 返回扣款结果:5.4
Request [method=ZhiFuBao, servieName=JingDong, param=183] 返回扣款结果:9.15
Request [method=ZhiFuBao, servieName=TianMao, param=223] 返回扣款结果:11.15
一共扣款了多少钱?135.87
消耗时间:3029毫秒!

可以发现,实现了效率的提升,并且任务的提交和任务的执行是分开来的。

但是也有缺点,就是程序太复杂,在多线程中还可以用Callable/Future的方式来实现这个例子,下一章介绍。

你可能感兴趣的:(多线程)