java 并行处理

参考链接

  • http://blog.dyngr.com/blog/2016/09/15/java-forkjoinpool-internals/
  • https://www.jianshu.com/p/bb5105303d85

概述

所谓的并行处理其目的就是为了提升原先串行处理的速度,从原理上来讲提升最明显的属于串行处理的逻辑之间没有共享资源的情况(或则说没有资源竞争的情况存在,基于多核服务器环境下);
从本质上看,其实现方式是将单线程任务切分为多个分片,由不同的线程来处理各个分片,充分利用资源以达提升处理速度的目的;由于使用多线程处理方式,故必定会增加线程间的通信(如每个线程的结果状态及结果数据的汇总)
原串行逻辑:
A -> B -> C -> D -> END
并行逻辑:
A
B
-> END
C
D

JAVA实现方式

  • 多线程方式
    启动多个线程进行任务处理,待处理完成后进行汇总
    简单代码:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

public class ParallelTask {

    private final static int DEALCOUNT = 10;
    private final static ExecutorService exec = Executors.newCachedThreadPool();
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ParallelTask parallel = new ParallelTask();
        List> futureList = new ArrayList>();
        long start_ts = System.nanoTime();
        for (int i = 0; i < DEALCOUNT; i++) {
            final int taskNum = i;
            futureList.add(exec.submit(parallel.new Task(taskNum)));
        }
        List retList = new ArrayList();
        ThreadPoolExecutor checkExec = (ThreadPoolExecutor)exec;
        while(checkExec.getCompletedTaskCount() < futureList.size()){
            Thread.sleep(10L);
        }
        for(Future f : futureList){
            retList.add(f.get());
        }
        long end_ts = System.nanoTime();
        System.out.println("Speed times : " + (end_ts - start_ts)/1000000);
        exec.shutdown();
        for(String s : retList){
            System.out.println(s);
        }
    }
    
    class Task implements Callable{
        
        private int taskNum;
        
        public Task(int num){
            this.taskNum = num;
        }

        @Override
        public String call() throws Exception {
            Thread.sleep(1000L);
            return taskNum + "_SUCC.";
        }
    }
}
  • CountDownLatch
    允许一个或多个线程一直等待,直到其他线程执行完后再执行;通过一个计数器来实现的,计数器的初始化值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就相应得减1。当计数器到达0时,表示所有的线程都已完成任务,然后在闭锁上等待的线程就可以恢复执行任务。
    伪代码:
Main thread start
Create CountDownLatch for N threads
Create and start N threads
Main thead wait on latch
N threads completes there tasks are returns
Main thread resume execution

简单代码1:

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class CountDownLatchDemo {
    private final static int DEALCOUNT = 10;
    private final static ExecutorService exec = Executors.newCachedThreadPool();
    public static void main(String[] args) throws Exception {
        long start_ts = System.nanoTime();
        final CountDownLatch countDownLatch = new CountDownLatch(DEALCOUNT);
        Map dealRet = new HashMap();
        for (int i = 0; i < DEALCOUNT; i++) {
            final int threadNum = i;
            exec.execute(() -> {
                try {
                    doTask(threadNum,dealRet);
                } catch (Exception e) {
                    System.out.println("exception : " + e.getMessage());
                } finally {
                    countDownLatch.countDown();
                }
            });
        }
        //等待处理结束
        countDownLatch.await();//全部结束
        //countDownLatch.await(2, TimeUnit.SECONDS);//等待一段时间
        long end_ts = System.nanoTime();
        System.out.println("DEAL END;Speed times : " + (end_ts - start_ts)/1000000 );
        exec.shutdown();
        //show result
        showResult(dealRet);
    }
    
    private static void doTask(int threadNum,Map dealRet) throws Exception {
        Thread.sleep(3000);
        System.out.printf("Deal No : %s\n", threadNum);
        if( null != dealRet ){
            dealRet.put(threadNum, "SUCC");
        }
        Thread.sleep(100);
    }
    
    private static void showResult(Map retMap){
        if( null != retMap ){
            Iterator> entries = retMap.entrySet().iterator();
            while( entries.hasNext() ){
                Map.Entry entry = entries.next();
                System.out.println("Key : " + entry.getKey() + " | Value : " + entry.getValue());
            }
        }
    }
}

简单代码2:

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchTask {
    private final static int DEALCOUNT = 10;
    private final static ExecutorService exec = Executors.newCachedThreadPool();
    public static void main(String[] args) throws Exception {
        long start_ts = System.nanoTime();
        final CountDownLatch countDownLatch = new CountDownLatch(DEALCOUNT);
        Map dealRet = new HashMap();
        CountDownLatchTask svr = new CountDownLatchTask();
        for (int i = 0; i < DEALCOUNT; i++) {
            final int threadNum = i;
            WorkTask work = svr.new WorkTask();
            Object[] methArgs = new Object[]{threadNum};
            Class[] parameterTypes = new Class[]{int.class};
            String methodName = "doTask";
            String retKey = threadNum + "_ret";
            work.initTask(svr, methArgs, parameterTypes, methodName, retKey, dealRet, countDownLatch);
            exec.execute(work);
        }
        //等待处理结束
        countDownLatch.await();//全部结束
        //countDownLatch.await(2, TimeUnit.SECONDS);//等待一段时间
        long end_ts = System.nanoTime();
        System.out.println("DEAL END;Speed times : " + (end_ts - start_ts)/1000000 );
        exec.shutdown();
        //show result
        showResult(dealRet);
    }
    
    class WorkTask implements Runnable{
        private CountDownLatch countDownLatch = null;
        private T obj;
        private Object[] args;
        private Class[] parameterTypes;
        private String methodName;
        private String retKey;
        private Map retMap;
        
        public void initTask(T t,Object[] args,Class[] parameterTypes,String method,String retKey,Map retMap,CountDownLatch latch){
            this.obj = t;
            this.args = args;
            this.parameterTypes = parameterTypes;
            this.methodName = method;
            this.retKey = retKey;
            this.retMap = retMap;
            this.countDownLatch = latch;
        }
        
        @Override
        public void run() {
            try{
                Method method = this.obj.getClass().getMethod(methodName, parameterTypes);
                Object retObj = method.invoke(this.obj, args);
                if( null != retMap && null != retKey ){
                    retMap.put(retKey, retObj);
                }
            }catch (Exception e){
                System.out.println("Task Run Exception." + e.getMessage());
            }finally{
                if( null != countDownLatch ){
                    countDownLatch.countDown();
                }
            }   
        }
    }
    
    public String doTask(int threadNum) throws Exception {
        Thread.sleep(3000);
        return threadNum + "_SUCC.";
    }
    
    private static void showResult(Map retMap){
        if( null != retMap ){
            Iterator> entries = retMap.entrySet().iterator();
            while( entries.hasNext() ){
                Map.Entry entry = entries.next();
                System.out.println("Key : " + entry.getKey() + " | Value : " + entry.getValue());
            }
        }
    }
}
  • ForkJoinPool(JAVA 7)
    用于实现“分而治之”的算法,特别是分治之后递归调用的函数,最适合的是计算密集型的任务;fork/join框架的目的是以递归方式将可以并行执行的任务拆分成更小的任务,然后将每个子任务的结果合并起来生成整体结果。
    代码样例1:
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;

public class ForkJoinPoolDemo {
    
    private static ForkJoinPool pool = new ForkJoinPool(4);

    public static void main(String[] args) throws InterruptedException {
         long[] numbers = LongStream.rangeClosed(1, 1000).toArray();
         long start_ts = System.nanoTime();
         long lRet = pool.invoke(new SumTask(numbers,0,numbers.length-1));
         long end_ts = System.nanoTime();
         System.out.println(pool.getPoolSize());//线程池的大小
         System.out.println("Result : " + lRet + " | Speed : " + (end_ts - start_ts)/1000000);
         lRet = 0;
         start_ts = System.nanoTime();
         for(long lNum : numbers){
             lRet += lNum;
            //其他计算任务
             Thread.sleep(10L);
         }
         end_ts = System.nanoTime();
         System.out.println("Result : " + lRet + " | Speed : " + (end_ts - start_ts)/1000000);
         
    }
    
    private static class SumTask extends RecursiveTask {
        private long[] numbers;
        private int from;
        private int to;

        public SumTask(long[] numbers, int from, int to) {
            this.numbers = numbers;
            this.from = from;
            this.to = to;
        }

        @Override
        protected Long compute() {
            // 当需要计算的数字小于32时,直接计算结果
            if (to - from < 32) {
                long total = 0;
                for (int i = from; i <= to; i++) {
                    total += numbers[i];
                    try {
                        //其他计算任务
                        Thread.sleep(10L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return total;
            // 否则,把任务一分为二,递归计算
            } else {
                int middle = (from + to) / 2;
                SumTask taskLeft = new SumTask(numbers, from, middle);
                SumTask taskRight = new SumTask(numbers, middle+1, to);
                taskLeft.fork();//开启一个新线程(或是重用线程池内的空闲线程),将任务交给该线程处理
                taskRight.fork();
                return taskLeft.join() + taskRight.join();//等待该任务的处理线程处理完毕,获得返回值
            }
        }
    }
}
  • Parallel Stream(JAVA 8)
    通过parallel()方法,将顺序执行的流转变成一个并行流来处理;并行流就是将一个内容分成多个数据块,并用不同的线程分别处理每个数据块,最后合并每个数据块的结果;其实现机制也是使用到fork/join框架;
    简单代码:
import java.util.stream.LongStream;
import java.util.stream.Stream;

public class ParallelStreamDemo {
    public static void main(String[] args) throws InterruptedException {
        long dealNum = 10000 * 1000l;
        doSumIterate(dealNum);
        Thread.sleep(1000L);
        doSumRange(dealNum);
        Thread.sleep(1000L);
        doSumByForEach(dealNum);
    }
    
    public static void doSumIterate(long n){
        long start_ts = System.nanoTime();
        Long lRet = Stream.iterate(1L, i -> i + 1).limit(n).reduce(0L, Long::sum);
        long end_ts = System.nanoTime();
        System.out.println("Sum From 0 To " + n  + " Result : " + lRet + ";Speed : " + (end_ts-start_ts)/1000000);
    }
    
    public static void doSumRange(long n){
        long start_ts = System.nanoTime();
        Long lRet = LongStream.rangeClosed(1, n).parallel().reduce(0L, Long::sum);
        long end_ts = System.nanoTime();
        System.out.println("Sum From 0 To " + n  + " Result : " + lRet + ";Speed : " + (end_ts-start_ts)/1000000);
    }
    
    public static void doSumByForEach(long n){
        long[] numbers = LongStream.rangeClosed(1, n).toArray();
        long start_ts = System.nanoTime();
        Long lRet = 0L;
        for(long lNum : numbers){
             lRet += lNum;
         }
        long end_ts = System.nanoTime();
        System.out.println("Sum From 0 To " + n  + " Result : " + lRet + ";Speed : " + (end_ts-start_ts)/1000000);
    }
}

你可能感兴趣的:(java 并行处理)