java多线程编程---Executor 技术学习

Executor技术UML图:
java多线程编程---Executor 技术学习_第1张图片
说明:
Executor: 最基础的父类,定义了一个接收Runnable对象的方法executor,其方法签名为executor(Runnable command)
ExecutorService: 比Executor使用更广泛的子类接口,其提供了生命周期管理的方法(运行,关闭,终止)。还提供可跟踪一个或多个异步任务执行状况返回Future的方法。
AbstractExecutorService: ExecutorService执行方法的默认实现
ScheduledExecutorService: 一个可定时调度任务的接口
ScheduledThreadPoolExecutor: ScheduledExecutorService的实现,一个可定时调度任务的线程池。
ThreadPoolExecutor: 线程池,可以通过调用Executors以下静态工厂方法来创建线程池并返回一个ExecutorService对象。

简单的例子:
ExecutorService executor = Executors.newSingleThreadExecutor();//使用Executors创建单个线程的executor
executor.execute(new Runnable(){//执行runable
@Override
public void run() {
System.out.println("executor test");
}
});

1. 线程池的相关
1) ThreadPoolExecutor构造函数的各个参数说明
构造函数各个参数如下:
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) //后两个参数为可选参数
参数说明:
corePoolSize:核心线程数,如果运行的线程少于corePoolSize,则创建新线程来执行新任务,即使线程池中的其他线程是空闲的
maximumPoolSize:最大线程数,可允许创建的线程数,corePoolSize和maximumPoolSize设置的边界自动调整池大小:
corePoolSize <运行的线程数< maximumPoolSize:仅当队列满时才创建新线程
corePoolSize=运行的线程数= maximumPoolSize:创建固定大小的线程池
keepAliveTime:如果线程数多于corePoolSize,则这些多余的线程的空闲时间超过keepAliveTime时将被终止
unit:keepAliveTime参数的时间单位
workQueue:保存任务的阻塞队列,与线程池的大小有关:
  当运行的线程数少于corePoolSize时,在有新任务时直接创建新线程来执行任务而无需再进队列
  当运行的线程数等于或多于corePoolSize,在有新任务添加时则选加入队列,不直接创建线程
  当队列满时,在有新任务时就创建新线程
threadFactory:使用ThreadFactory创建新线程,默认使用defaultThreadFactory创建线程
handle:定义处理被拒绝任务的策略,默认使用ThreadPoolExecutor.AbortPolicy,任务被拒绝时将抛出RejectExecutorException

2) Executors提供一系列的静态工厂方法用于创建各种线程池的ExecutorService对象
  newFixedThreadPool :创建可重用且固定线程数的线程池,如果线程池中的所有线程都处于活动状态,此时再提交任务就在队列中等待,直到有可用线程;如果线程池中的某个线程由于异常而结束时,线程池就会再补充一条新线程。
 public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  //使用一个基于FIFO排序的阻塞队列,在所有corePoolSize线程都忙时新任务将在队列中等待
                                  new LinkedBlockingQueue());
}
newSingleThreadExecutor:创建一个单线程的Executor,如果该线程因为异常而结束就新建一条线程来继续执行后续的任务
 public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  //使用一个基于FIFO排序的阻塞队列,在所有corePoolSize线程都忙时新任务将在队列中等待
                                  new LinkedBlockingQueue());
}
   newScheduledThreadPool:创建一个可延迟执行或定期执行的线程池
例子:
public class HeartBeat {
    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
        Runnable task = new Runnable() {
            public void run() {
                System.out.println("HeartBeat.........................");
            }
        };
        executor.scheduleAtFixedRate(task,5,3, TimeUnit.SECONDS);   //5秒后第一次执行,之后每隔3秒执行一次
    }
} 
  newCachedThreadPool:创建可缓存的线程池,如果线程池中的线程在60秒未被使用就将被移除,在执行新的任务时,当线程池中有之前创建的可用线程就重      用可用线程,否则就新建一条线程
public static ExecutorService newCachedThreadPool() {    
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,                                  //使用同步队列,将任务直接提交给线程
                                  new SynchronousQueue());}
例子:
public class ThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
     ExecutorService threadPool = Executors.newCachedThreadPool();//线程池里面的线程数会动态变化,并可在线程线被移除前重用
        for (int i = 1; i <= 3; i ++) {
            final  int task = i;   //10个任务
            //TimeUnit.SECONDS.sleep(1);
            threadPool.execute(new Runnable() {    //接受一个Runnable实例
                public void run() {
                        System.out.println("线程名字: " + Thread.currentThread().getName() +  "  任务名为: "+task);
                }
            });
        }
    }
}
输出结果:
输出(为每个任务新建一条线程,共创建了3条线程)
线程名字: pool-1-thread-1 任务名为: 1
线程名字: pool-1-thread-2 任务名为: 2
线程名字: pool-1-thread-3 任务名为: 3
去掉第6行的注释其输出如下:(始终重复利用一条线程,因为newCachedThreadPool能重用可用线程)
线程名字: pool-1-thread-1 任务名为: 1
线程名字: pool-1-thread-1 任务名为: 2
线程名字: pool-1-thread-1 任务名为: 3
2. Executor的生命周期
ExecutorService提供了管理Executor生命周期的方法,ExecutorService的生命周期包括了:运行,关闭和终止的三种状态。
ExecutorService在初始化创建时处于运行状态
executor.shutdown(): 等待提交的任务执行完成并不再接收新任务时关闭。
executor.shutdownNow(): 强制终止所有运行中的任务,并不再允许提交新任务。
例子:
//创建10个固定线程池的executorService对象
ExecutorService executor = Executors.Executors.newFixedThreadPool(10);

for(int i = 0; i <= 300; i++) {
    final  int t = i;
    Runnable task = new Runnable() {

        @Override
        public void run() {

            System.out.println("task over" + t);
        }
    };
    executor.execute(task);
}
executor.shutdown();//等待所有任务执行完时关闭。
3. 使用Callable, Future返回结果
ExecutorService的submit方法可以把Runnable或者Callable提交,返回Future对象,再通过此对象调用get方法获取执行任务结果,并且这个执行是阻塞的(只有当Runnable或者Callable里面的内容执行完成才执行)。
例子:
try {
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    Future future = executorService.submit(new Callable() {
        @Override
        public String call() throws Exception {
            return "MOBIN";
        }
    });

    System.out.println("任务的执行结果:"+future.get());
    executorService.shutdown();
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}
下面是使用Future的get方法来获取结果,来实现并行计算框架。
package com.wm.effectivejava.test;
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.FutureTask;

public class ConcurrentCalculator {

    private ExecutorService exec;
    private int cpuCoreNumber;
    private List> tasks = new ArrayList>();

    // 内部类
    class SumCalculator implements Callable {
        private int[] numbers;
        private int start;
        private int end;

        public SumCalculator(final int[] numbers, int start, int end) {
            this.numbers = numbers;
            this.start = start;
            this.end = end;
        }

        public Long call() throws Exception {
            Long sum = 0l;
            for (int i = start; i < end; i++) {
                sum += numbers[i];
            }
            return sum;
        }
    }

    public ConcurrentCalculator() {
        cpuCoreNumber = Runtime.getRuntime().availableProcessors();
        exec = Executors.newFixedThreadPool(cpuCoreNumber);
    }

    public Long sum(final int[] numbers) {
        // 根据CPU核心个数拆分任务,创建FutureTask并提交到Executor
        for (int i = 0; i < cpuCoreNumber; i++) {
            int increment = numbers.length / cpuCoreNumber + 1;
            int start = increment * i;
            int end = increment * i + increment;
            if (end > numbers.length)
                end = numbers.length;
            SumCalculator subCalc = new SumCalculator(numbers, start, end);
            FutureTask task = new FutureTask(subCalc);
            tasks.add(task);
            if (!exec.isShutdown()) {
                exec.submit(task);
            }
        }
        return getResult();
    }

    /**
     * 迭代每个只任务,获得部分和,相加返回
     *
     * @return
     */
    public Long getResult() {
        Long result = 0l;
        for (Future task : tasks) {
            try {
                // 如果计算未完成则阻塞
                Long subSum = task.get();
                result += subSum;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    public void close() {
        exec.shutdown();
    }

    public static void main(String[] args){
        int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 10, 11};
        ConcurrentCalculator calc = new ConcurrentCalculator();
        Long sum = calc.sum(numbers);
        System.out.println(sum);
        calc.close();
    }
}
上例中,Future对象.get方法会阻塞,如果前面没有计算完会等计算完后,才能计算后面,会影响计算的性能。
CompletionService可以解决此问题,他是对Executor对象进行封装,并把任务完成的结果会自动收集起来,再使用take()获取已经完成的任务,并且按照完成任务顺序处理结果。也就是调用CompletionService的take方法是,会返回按完成顺序放回任务的结果,CompletionService内部维护了一个阻塞队列BlockingQueue,如果没有任务完成,take()方法也会阻塞。
修改刚才例子如下:
package com.wm.effectivejava.test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class ConcurrentCalculator {

    private ExecutorService exec;
    private int cpuCoreNumber;
    //private List> tasks = new ArrayList>();
    private CompletionService completionService;

    // 内部类
    class SumCalculator implements Callable {
        private int[] numbers;
        private int start;
        private int end;

        public SumCalculator(final int[] numbers, int start, int end) {
            this.numbers = numbers;
            this.start = start;
            this.end = end;
        }

        public Long call() throws Exception {
            Long sum = 0l;
            for (int i = start; i < end; i++) {
                sum += numbers[i];
            }
            return sum;
        }
    }

    public ConcurrentCalculator() {
        cpuCoreNumber = Runtime.getRuntime().availableProcessors();
        exec = Executors.newFixedThreadPool(cpuCoreNumber);
        completionService = new ExecutorCompletionService(exec);
    }

    public Long sum(final int[] numbers) {
        // 根据CPU核心个数拆分任务,创建FutureTask并提交到Executor
        for (int i = 0; i < cpuCoreNumber; i++) {
            int increment = numbers.length / cpuCoreNumber + 1;
            int start = increment * i;
            int end = increment * i + increment;
            if (end > numbers.length)
                end = numbers.length;
            SumCalculator subCalc = new SumCalculator(numbers, start, end);
            //FutureTask task = new FutureTask(subCalc);
            //tasks.add(task);
            if (!exec.isShutdown()) {
                completionService.submit(subCalc);
            }
        }
        return getResult();
    }

    /**
     * 迭代每个只任务,获得部分和,相加返回
     *
     * @return
     */
    public Long getResult() {
        Long result = 0l;
        for (int i = 0; i < cpuCoreNumber; i++) {
            try {
                // 如果计算未完成则阻塞
                Long subSum = completionService.take().get();
                result += subSum;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    public void close() {
        exec.shutdown();
    }

    public static void main(String[] args){
        int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 10, 11};
        ConcurrentCalculator calc = new ConcurrentCalculator();
        Long sum = calc.sum(numbers);
        System.out.println(sum);
        calc.close();
    }
}




你可能感兴趣的:(java)