Java并发之执行器 —— 线程池ThreadPool

 

优势

  1. 构建一个新的线程是有一定代价的, 因为涉及与操作系统的交互。如果程序中创建了大量的生命期很短的线程,应该使用线程池
  2. 减少并发线程的数目

Executors

执行器( Executors) 类有许多静态工厂方法用来构建线程池

方法描述
newCachedThreadPool必要时创建新线程;空闲线程会被保留 60 秒
newFixedThreadPool该池包含固定数量的线程;空闲线程会一直被保留
newSingleThreadExecutor只有一个线程的 “ 池”, 该线程顺序执行每一个提交的任务(类似于 Swing 事件分配线程)
newScheduledThreadPool用于预定执行而构建的固定线程池, 替代 java.util.Timer
newSingleThreadScheduledExecutor用于预定执行而构建的单线程 “ 池”
newWorkStealingPool (1.8新增)创建一个线程池,该线程池维护足够的线程以支持给定的并行级别,并且可以使用多个队列来减少争用。并行级别对应于主动参与或可用于进行任务处理的最大线程数。线程的实际数量可以动态地增长和收缩。工作窃取池不能保证执行提交的任务的顺序。

重点介绍下列三种

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }
 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
  1. newCachedThreadPool 方法构建了一个线程池,初始线程数0,对于每个任务,如果有空闲线程可用,立即让它执行任务,如果没有可用的空闲线程,则创建一个新线程。60秒后回收不使用的线程。
  2. newFixedThreadPool 方法构建一个具有固定大小的线程池。 如果提交的任务数多于空闲的线程数,那么把得不到服务的任务放置到队列中。当其他任务完成以后再运行它们。线程使用完立即回收。
  3. newSingleThreadExecutor 是一个退化了大小为 1 的线程池: 由一个线程执行提交的任务,一个接着一个。

这3个方法返回实现了ExecutorService 接口ThreadPoolExecutor 类的对象。再将一个 Runnable 对象或 Callable 对象提交给 ExecutorService得到Future

ExecutorService service = Executors.newFixedThreadPool(10);
Callable<Integer> callable = new Callable<Integer>(){
	@Override
	public Integer call() throws Exception {
	    return 1;
	}
};
Future<Integer> future = service.submit(callable);
int i = 0;
try {
	i = future.get();
} catch (InterruptedException | ExecutionException e) {
	e.printStackTrace();
}
	System.out.println(i);

ExecutorService

  • Future<?> submit(Runnable task)  使用这样一个对象来调用 isDone、 cancel 或 isCancelled。但是, get 方法在完成的时候只是简单地返回 null。
  • Future<T> submit(Runnable task, T result) Future 的 get 方法在完成的时候返回指定的 result 对象。
  • Future<T> submit(Callable<T> task) 返回的 Future 对象将在计算结果准备好的时候得到它。
  • void shutdown() 关闭服务, 会先完成已经提交的任务而不再接收新的任务。

 

ThreadPoolExecutor

使用ThreadPoolExecutor对象构造线程池。

我们观察Executors的newCachedThreadPool源码,内部实例化了一个ThreadPoolExecutor对象。

观察ThreadPoolExecutor构造方法

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

从结构关系上分析ThreadPoolExecutor继承了AbstractExecutorService抽象类,并实现了ExecutorService接口

从构造参数上分析

  • corePoolSize: corePoolSize线程池中要保持的数量,即使它们是空闲的,除非设置了allowCoreThreadTimeOut属性为true
  • maximumPoolSize:最大允许在池中允许的最大线程数。
  • keepAliveTime:当线程的数量大于核心时,这是空闲线程在终止之前等待新任务的最大时间。(超过核心线程数的线程如果长时间获得不到新任务就会终止掉)
  • unit : keepAliveTime的单位 如TimeUnit.SECONDS 代表秒
  • workQueue:工作队列(实现BlockingQueue接口的阻塞队列)用于在执行任务之前保存任务的队列。此队列只保留execute方法提交的Runnable任务。常使用的阻塞队列LinkedBlockingQueue、SynchronousQueue、ArrayBlockingQueue。使用SynchronousQueue队列表示,没有缓存队列在存储多余的线程。

 

线程的排队策略与阻塞队列相关:

LinkedBlockingQueue:当任务数大于核心线程数时,新任务添加到队列中,当队列容量达到最大时,将创建新线程,直到达到最大线程数maximumPoolSize。(基于链表实现的一个阻塞队列,默认容器大小Integer.MAX_VALUE。)

SynchronousQueue: 当任务数大于核心线程数时,直接创建新的线程直到达到最大线程数maximumPoolSize。(一个没有内部容量的阻塞队列,队列中每个元素的插入操作必须等待另一个线程执行相应的移除操作。可以理解为没有缓存)

ArrayBlockingQueue : 同LinkedBlockingQueue。(基于数组实现的一个阻塞队列,需要指定容器大小。)

  • threadFactory:创建新线程时要使用的工厂。
  • handler : 处理程序在执行被阻塞时使用的处理程序,因为达到了线程边界和队列容量。

handler有以下四种策略:

ThreadPoolExecutor.AbortPolicy():丢弃任务并抛出RejectedExecutionException异常。 (ThreadPoolExecutor默认)
ThreadPoolExecutor.CallerRunsPolicy():由调用线程处理该任务 。
ThreadPoolExecutor.DiscardPolicy():也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy():丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

小结

使用ThreadPoolExecutor构造方法去生成线程池更加灵活,我们可以灵活指定核心线程数,最大线程数,工作队列及空闲线程保存时间。

构造一个核心线程数为5,最大线程数为10,缓存任务数为5,线程超时为2秒的线程池。

ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10, 2000, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(5));


 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值