JDK中Fork和Join框架

1.Fork和Join简介

 

Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

 

Fork:把一个大任务分成若干个子任务进行执行,设置一个阀值,判断任务数量,如果超越阀值,无线分割下去

Join:合并子任务获得最终结果

 

例子:1+2+3+...+9999+10000,通过Fork将其分成10分,每份计算1000个数的加法运算

 

流程图:

2.工作窃取算法:

    简单的来讲 就是 讲一个大任务切分成若干份小任务,每个任务放入到不同的队列当中,每个队列有一个相应的工作线程来执行这些任务,当这些线程执行完当前的任务,线程闲置,为了提高效率以及避免浪费资源,利用这些线程窃取其他队列中的任务来执行已达到高效的目的

 

    队列一般设置成双端队列:当前队列中的线程从队列的首端获取任务执行,而窃取的线程从队列的尾端获取任务执行,这种方式也有缺点,当队列中只有一个任务的时候,会造成资源竞争以及创建多个双端队列

3.Fork和Join两个主要的类

    --ForkJoinTask

        创建forkjoin任务,此类在任务中执行fork和join操作的机制,通常继承他的两个子类

            --RecursiveAction(没有返回结果)

            --RecursiveTask(有返回结果)

    --ForkJoinPool

        任务需要通过此类来执行,人物分割出来的子任务会添加到当前的工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,他会随机从其他工作线程的队列的尾部获取任务

 

为什么要是用fork/join框架?

    --多核处理器的执行效率

    --普通的多线程相对复杂

    --高效利用多核平台硬件

    --避免死锁(死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程)

4.举例

--使用fork/join计算1+2+3+4+5+6

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

package com.glorze.forkjoin;

 

import java.util.concurrent.ForkJoinPool;

import java.util.concurrent.Future;

import java.util.concurrent.RecursiveTask;

 

/**

* 高老四博客

http://glorze.com

* @Description: 计算1+2+3+4+5+6  

@author   

@date 2017年7月3日 下午11:22:22

 */

public class ForkJoin extends RecursiveTask<Integer>{

 

    //设定一个阀值

    private static final int value = 3;

     

    private int start;

    private int end;

     

    public ForkJoin(int start, int end) {

        super();

        this.start = start;

        this.end = end;

    }

 

    @Override

    protected Integer compute() {

        int sum = 0;

        //如果任务小于当前阀值就计算任务,无需拆分

        boolean canCompute = (end - start) <= value;

        if(canCompute){

            for(int i = start; i<= end; i++){

                sum += i;

            }

        }else{

            //如果任务大鱼阀值,裂变成两个任务

            int middle = (start + end) / 2;

            ForkJoin leftForkJoin = new ForkJoin(start, middle);

            ForkJoin rightForkJoin = new ForkJoin(middle, end);

            //分别执行两个子任务

            leftForkJoin.fork();

            rightForkJoin.fork();

            //合并子任务

            int leftResult = leftForkJoin.join();

            int rightResult = rightForkJoin.join();

             

            sum = leftResult + rightResult;

        }

        return sum;

    }

     

    public static void main(String[] args) {

        ForkJoinPool forkJoinPool = new ForkJoinPool();

        //计算1+2+3+4+5+6

        ForkJoin forkJoin = new ForkJoin(16);

        //执行

        Future<Integer> result = forkJoinPool.submit(forkJoin);

        try {

            System.out.println(result.get());

        catch (Exception e) {

            e.printStackTrace();

        }

    }

     

}

5.站长所在公司使用forkjoin的封装,其实也是forkjoin的原理改装

生成大量优惠券(适配器模式)

首先,创建fork的接口

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

package com.liuliangqianbao.util.forkjoin;

 

import java.util.Map;

 

public interface MethodAdapter<P,V> {

 

    /**

     * 判断是否可以执行该任务

     @param data   要执行的任务数据

     * @return        返回是否可执行

     */

    public boolean canCompute(P data);

     

    /**

     * 执行任务

     * @param data           要执行的任务数据   

     * @param otherParam 其他参数

     * @return                执行的结果

     */

    public V compute(P data, Map<String, Object> param);

     

    /**

     * 当数据不符合阈值时,对数据进行拆分后,左起的数据

     * @param data   要执行的任务数据   

     * @return        拆分后的左起数据

     */

    public P leftData(P data);

     

    /**

     * 当数据不符合阈值时,对数据进行拆分后,右侧的数据

     * @param data   要执行的任务数据   

     * @return        拆分后的右侧数据

     */

    public P rightData(P data);

     

}

实现接口实现计算的封装 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

package com.liuliangqianbao.util.forkjoin;

 

import java.util.List;

import java.util.Map;

 

public abstract class ListParamMethodAdapter<V> implements MethodAdapter<List, V> {

 

    protected int threshold;      //集合的阈值

    public ListParamMethodAdapter(int threshold) {

        super();

        this.threshold = threshold;

    }

     

    /**

     * 是否可计算

     */

    @Override

    public boolean canCompute(List data) {

        return (data.size()<=threshold)? true:false;

    }

 

    /**

     * 执行计算(带额外参数)

     */

    public abstract V compute(List data, Map<String, Object> param);

     

    /**

     * 数据拆分后的左侧数据

     */

    @Override

    public List leftData(List data) {

        return data.subList(0, threshold);

    }

 

    /**

     * 数据拆分后的右侧数据

     */

    @Override

    public List rightData(List data) {

        return data.subList(threshold, data.size());

    }

     

}

封装工具类,调用ListParamMethod

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

package com.liuliangqianbao.util.forkjoin;

 

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

import java.util.Map;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ForkJoinPool;

import java.util.concurrent.Future;

import java.util.concurrent.RecursiveTask;

/**

 * 多线程并发执行任务工具类

 * 高老四博客 http://glorze.com

 * @ClassName ForkJoinTaskUtil 

 * @author Glorze

 * @createDate 2017年1月15日下午6:31:04

 */

public class ForkJoinTaskUtil<P,V> {

     

    private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");

     

    private P data;

     

    private Map<String, Object> param;

     

    private MethodAdapter<P,V> adapter;

     

    public ForkJoinTaskUtil(P data, Map<String, Object> param, MethodAdapter<P,V> adapter) {

        super();

        this.data = data;

        this.param = param;

        this.adapter = adapter;

    }

     

    public List<V> computeResult(){

        ForkJoinPool pool = new ForkJoinPool();

        //生成计算任务

        ComputeTask<P,V> task = new ComputeTask<P,V>(data, param, adapter);

        //执行一个计算任务

        Future<List<V>> future = pool.submit(task);

        //获取计算结果

        try {

            Date start = new Date();

            Util.log(" 多线程并发工具类[主线程]开始启用计算任务,当前时间:"+format.format(start));

            List<V> result = future.get();

            Date end = new Date();

            Util.log(" 多线程并发工具类[主线程]合并计算结果完毕,当前时间:"+format.format(end)+",共耗时:"+(end.getTime()-start.getTime())+"ms!");

            return result;

        catch (InterruptedException | ExecutionException e) {

            e.printStackTrace();

        }

        return null;

    }

     

     

     

    protected class ComputeTask<P,V> extends RecursiveTask<List<V>>{

         

        private static final long serialVersionUID = 1L;

         

        private P data;

         

        private Map<String, Object> param;

         

        private MethodAdapter<P,V> adapter;

         

        public ComputeTask(P data, Map<String, Object> param, MethodAdapter<P,V> adapter) {

            super();

            this.data = data;

            this.param = param;

            this.adapter = adapter;

        }

         

        @Override

        protected List<V> compute() {

            List<V> result = new ArrayList<V>();

             

            if(adapter.canCompute(data)){

                //任务够小直接执行

                Date start = new Date();

                Util.log("   ["+Thread.currentThread()+"]开始计算任务,当前时间:"+format.format(start));

                V obj = adapter.compute(data, param);

                Date end = new Date();

                Util.log("   ["+Thread.currentThread()+"]执行计算任务完毕,当前时间:"+format.format(end)+",共耗时:"+(end.getTime()-start.getTime())+"ms!");

                result.add(obj);

            }else{

                //任务超过阈值,分割任务

                Util.log("   ["+Thread.currentThread()+"]开始分割任务");

                ComputeTask<P,V> left = new ComputeTask<P,V>(adapter.leftData(data), param, adapter);

                ComputeTask<P,V> right = new ComputeTask<P,V>(adapter.rightData(data), param, adapter);

                //执行子任务

                Date start = new Date();

                Util.log("   ["+Thread.currentThread()+"]开始启用分割后的子任务,当前时间:"+format.format(start));

                left.fork();

                right.fork();

                //等待子任务执行完

                List<V> leftRes = left.join();

                List<V> rightRes = right.join();

                Date end = new Date();

                Util.log("   ["+Thread.currentThread()+"]子任务执行完毕,当前时间:"+format.format(end)+",共耗时:"+(end.getTime()-start.getTime())+"ms!");

                //合并任务结果

                result.addAll(leftRes);

                result.addAll(rightRes);

            }

             

            return result;

        }

    }

     

}

优惠券生成入库

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

/**

 * 并发存储库存

 * @Description:高老四博客

 http://www.glorze.com

 * @author: Glorze

 * @since: 2016年12月5日 下午5:02:10

 */

@Override

public Map<String, Integer> saveMCInventoryThread(MerchantCoupon record) {

    //要操作的所有优惠券库存集合

    List<MerchantCouponInventoryDetail> data = new ArrayList<MerchantCouponInventoryDetail>();

    //拼装要生成的数据集合

 

    //获取merchantCouponInventoryDetail表code主键的最大值

 

    Map<String, Object> codeMap  = new HashMap<String, Object>();

    codeMap.put("couponSid", record.getCouponId());

    Long codeId=merchantCouponInventoryDetailMapper.findCodeMax(codeMap);

    if(codeId==null){

        codeId=(long0;

    }

 

    MerchantCouponInventoryDetail inventory;     //单条优惠券库存实体

    for(int i=0;i<record.getCreateNum();i++){

        //set各个公共的字段..

        inventory = new MerchantCouponInventoryDetail();

        inventory.setCode(codeId+i+1);

        inventory.setCouponSid(record.getCouponId());// 优惠券的sid

        inventory.setCouponType(record.getCouponType());// 优惠券类型

        inventory.setCouponName(record.getCouponName());// 优惠券标题

        inventory.setCustId(record.getCustId());// 商户ID

        inventory.setEffectStartTime(record.getEffectStartTime());// 生效时间

        inventory.setEffectEndTime(record.getEffectEndTime());// 失效时间

        inventory.setStatus("1");// 设置为生效状态

        inventory.setIsUse("2");// 设置为未核销状态

        if (record.getStoreInfoSids()!=null) {

            inventory.setCrmStoreInfoSid(Long.parseLong(record.getStoreInfoSids()));// 设置门店ID

        }

        inventory.setCreateTime(record.getCreateTime());// 创建日期

        data.add(inventory);

    }

 

    //构造业务调用封装对象

    ListParamMethodAdapter<Map<String, Integer>> adapter = new ListParamMethodAdapter<Map<String,Integer>>(200) {

        @Override

        public Map<String, Integer> compute(List data, Map<String, Object> param) {

            return insertInventDetails(data);

        }

    };

 

    //构造ForkJoinTaskUtil参数

    ForkJoinTaskUtil util = new ForkJoinTaskUtil(data, null, adapter);

    //获取执行结果

    List<Map<String, Integer>> result = util.computeResult();

 

    //合并计算结果

    Util.log(" [主线程]开始获取并合并计算结果..");

    int successcount = 0;

    int failcount = 0;

    for(Map<String, Integer> map:result){

        successcount += map.get("success");

        failcount += map.get("fail");

    }

    //打印执行结果

    Util.log("成功的数量为:"+successcount);

    Util.log("失败的数量为:"+failcount);

 

    Map<String, Integer> resultmap = new HashMap<String, Integer>();

    resultmap.put("successcount", successcount);

    resultmap.put("failcount", failcount);

    return resultmap;

}

 

 

/**

 * 单个线程要执行的业务操作

 *    循环要操作的数据集合

 *        拼装各个字段(动态生成二维码code,将二维码保存到mongodb服务器)

 *        将拼装完各字段的数据实体插入到mysql

 *    最后返回操作成功或者失败的条数

 * @Description: 高老四博客

 http://www.glorze.com

 * @author: Glorze

 * @since: 2016年12月5日 下午4:35:24

 */

public Map<String, Integer> insertInventDetails(List<MerchantCouponInventoryDetail> data){

 

    java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyMMddHHmmss");//生成12位随机数字

 

 

    //遍历操作数据

    for (MerchantCouponInventoryDetail inventory: data) {

        try{

            //set各优惠券库存不一样的属性>.. sid,code,二维码,核销连接URL

 

            String codeStr=String.format("%06d", inventory.getCode());

            String cryptCode=CouponCodeEncryptUtil.encryptData(Long.valueOf(inventory.getCouponSid()+codeStr));

            //生成sid

            inventory.setSid(UUIDGenerator.generaterLongKeyByNanoTime());

            //生成code

            inventory.setCryptCode(cryptCode);

            //生成核销连接URL以及二维码

            //              String hexiaocode = PropertiesUtils.getInitValue("wapHomeUrl") + "/card/recharge/"+data.get(0).getUseCode();

            String hexiaoUrl="";

            if(MerchantCouponInventoryDetail.COUPON_TYPE_FLOW.equals(inventory.getCouponType())){

                hexiaoUrl=PropertiesUtils.getInitValue("wapHomeUrl")+"/coupon/recharge/"+cryptCode;

            }else{

                hexiaoUrl = PropertiesUtils.getInitValue("httpHomeUrl") + "/coupon/verify/"+cryptCode;

            }

            inventory .setUseLinkUrl(hexiaoUrl);

            //保存二维码到mongodb服务器

//              byte[] code = QRCodeUtil.buildQRCode(hexiaoUrl);

            //保存二维码(没有白边)到mongodb服务器  

            byte[] code = QRCodeUtil.buildQRCodeNoWhite(hexiaoUrl, nullnullnull);

            String hexiaoQrcode = MongoDbClient.getInstance().writeFile(code);

            inventory .setUseLinkUrlImg(hexiaoQrcode);

        }catch (Exception e){

            e.printStackTrace();

        }

    }

 

    //批量插入数据

    int effectRows = merchantCouponInventoryDetailMapper.insertBatch(data);//批量插入数据库

 

    //返回操作成功,失败的条数

    Map<String, Integer> result = new HashMap<String, Integer>();

    result.put("success", effectRows);

    result.put("fail", data.size()-effectRows);

    return result;

}

转载于:https://my.oschina.net/tallblame/blog/1154619

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值