Java多线程(7)-- 分工之Fork/Join框架

正确的使用Fork/Join框架,需要一定熟悉它的结构,对于一个分布式的任务,必然具备两种条件:①任务调度;②任务执行。在Fork/Join中,我们主要用它自定义的线程池来提交任务和调度任务,称之为:ForkJoinPool;同时我们有它自己的任务执行类,称之为:ForkJoinTask。

    不过我们不直接使用ForkJoinTask来直接执行和分解任务,我们一般都使用它的两个子类,RecursiveAction和RecursiveTask,其中,前者主要处理没有返回结果的任务,后者主要处理有返回结果的任务。总结一下,以下就是Fork/Join的基本模型:

用代码实现Fork/Join实现大数据计算:

需求:计算1+2+3+……..+N的和

以下是用Fork/Join进行计算,主要的核心思想就是把超大的计算拆分为小的计算,通俗来说就是把一个极大的任务拆分为很多个小任务,下面是核心计算模型:

代码实现如下:

package com.brickworkers;

import java.util.concurrent.ForkJoinPool;

import java.util.concurrent.RecursiveTask;


public class FockJoinTest extends RecursiveTask{//继承RecursiveTask来实现

    //设立一个最大计算容量

    private final intDEFAULT_CAPACITY = 10000;

    //用2个数字表示目前要计算的范围

    private int start;

    private int end;


    public FockJoinTest(intstart, int end) {

        this.start = start;

        this.end = end;

    }


    @Override

    protected Long compute(){//实现compute方法

        //分为两种情况进行出来

        long sum = 0;

        //如果任务量在最大容量之内

        if(end - start

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

                sum += i;

            }

        }else{//如果超过了最大容量,那么就进行拆分处理

            //计算容量中间值

            int middle =(start + end)/2;

            //进行递归

            FockJoinTestfockJoinTest1 = new FockJoinTest(start, middle);

            FockJoinTestfockJoinTest2 = new FockJoinTest(middle + 1, end);

            //执行任务

           fockJoinTest1.fork();

           fockJoinTest2.fork();

            //等待任务执行并返回结果

            sum =fockJoinTest1.join() + fockJoinTest2.join();

        }


        return sum;

    }


    public static void main(String[] args) {

        ForkJoinPoolforkJoinPool = new ForkJoinPool();

        FockJoinTestfockJoinTest = new FockJoinTest(1, 100000000);

        longfockhoinStartTime = System.currentTimeMillis();

        //前面我们说过,任务提交中invoke可以直接返回结果

        long result = forkJoinPool.invoke(fockJoinTest);

       System.out.println("fock/join计算结果耗时"+(System.currentTimeMillis() - fockhoinStartTime));


        long sum = 0;

        long normalStartTime= System.currentTimeMillis();

        for (int i = 0; i< 100000000; i++) {

            sum += i;

        }

       System.out.println("普通计算结果耗时"+(System.currentTimeMillis() - normalStartTime));

    }

}


//执行结果:

//fock/join计算结果耗时33

//普通计算结果耗时141


Java并发包(java.util.concurrent)总结

        1)并发容器

             ConcurrentHashMap

              有序的ConcurrentSkipListMap

               线程安全的动态数组CopyOnWriteArrayList

        2)同步设备

              CountDownLatch

              CyclicBarrier

              Semaphore,可作为资源控制器,限制同时进行工作的线程数量

        3)各种并发队列

               BlockedQueue、ArrayBlockingQueue、SynchorousQueue或特定场景的

              PriorityBlockingQueue等

        4)原子对象

              AtomicInteger

              AtomicBoolean

              …

        5)锁

              ReentrantLock

              Condition

             ReentrantReadWriteLock

             ReentrantReadWriteLock.WriteLock

             ReentrantReadWriteLock.ReadLock

        6)执行器和线程池

             ExecutorService

             CompletionService

              ScheduledExecutor

              Callable

              FutureTask

        7)Fork-join 框架


 多线程开发良好的实践

* 给线程起个有意义的名字,这样可以方便找 Bug。

* 缩小同步范围,从而减少锁争用。例如对于 synchronized,应该尽量使用同步块而不是同步方法。

* 多用同步工具少用 wait() 和 notify()。首先,CountDownLatch, CyclicBarrier, Semaphore 和 Exchanger 这些同步类简化了编码操作,而用 wait() 和 notify() 很难实现复杂控制流;其次,这些同步类是由最好的企业编写和维护,在后续的 JDK 中还会不断优化和完善。

* 使用 BlockingQueue 实现生产者消费者问题。

* 多用并发集合少用同步集合,例如应该使用 ConcurrentHashMap 而不是 Hashtable。

* 使用本地变量和不可变类来保证线程安全。

* 使用线程池而不是直接创建线程,这是因为创建线程代价很高,线程池可以有效地利用有限的线程来启动任务。


参考书目:《Java并发编程实战》、《Java编程思想》、《Java核心技术卷一》、《Effective Java》、《Java并发编程的艺术》

你可能感兴趣的:(Java多线程(7)-- 分工之Fork/Join框架)