Java中五种线程池的介绍

来源:互联网 发布:数据挖掘的过程 编辑:程序博客网 时间:2024/05/19 17:07
Java中五种线程池的介绍
在Java源码中,构造方法如图:
/**     * Creates a new {@code ThreadPoolExecutor} with the given initial     * parameters and default thread factory and rejected execution handler.     * It may be more convenient to use one of the {@link Executors} factory     * methods instead of this general purpose constructor.     *     * @param corePoolSize the number of threads to keep in the pool, even     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set     * @param maximumPoolSize the maximum number of threads to allow in the     *        pool     * @param keepAliveTime when the number of threads is greater than     *        the core, this is the maximum time that excess idle threads     *        will wait for new tasks before terminating.     * @param unit the time unit for the {@code keepAliveTime} argument     * @param workQueue the queue to use for holding tasks before they are     *        executed.  This queue will hold only the {@code Runnable}     *        tasks submitted by the {@code execute} method.     * @throws IllegalArgumentException if one of the following holds:<br>     *         {@code corePoolSize < 0}<br>     *         {@code keepAliveTime < 0}<br>     *         {@code maximumPoolSize <= 0}<br>     *         {@code maximumPoolSize < corePoolSize}     * @throws NullPointerException if {@code workQueue} is null     */    public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue) {        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,             Executors.defaultThreadFactory(), defaultHandler);    }
简单介绍下参数含义:
第一个参数corePoolSize:线程池中所保存的线程数,核心线程,包括空闲线程,在初创建线程池时线程不会立即启动,直到有任务提交才开始启动线程并逐渐时线程数目达到corePoolSize。若想一开始就创建所有核心线程需调用prestartAllCoreThreads方法。

第二个参数maximumPoolSize:线程池中允许的最大线程数,需要注意的是当核心线程满且阻塞队列也满时才会判断当前线程数是否小于最大线程数,并决定是否创建新线程。 

第三个参数keepAliveTime当线程数大于核心时,多于的空闲线程最多存活时间 。

第四个参数unit:参数的时间单位。

第五个参数workQueue当线程数目超过核心线程数时用于保存任务的队列。主要有3种类型的BlockingQueue可供选择:无界队列,有界队列和同步移交。

首先看一下新任务进入时线程池的执行策略: 
如果运行的线程少于corePoolSize,则 Executor始终首选添加新的线程,而不进行排队。(如果当前运行的线程小于corePoolSize,则任务根本不会存入queue中,而是直接运行) 
如果运行的线程大于等于 corePoolSize,则 Executor始终首选将请求加入队列,而不添加新的线程。 
如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。 

主要有3种类型的BlockingQueue:
1.无界队列
队列大小无限制,常用的为无界的LinkedBlockingQueue,使用该队列做为阻塞队列时要尤其当心,当任务耗时较长时可能会导致大量新任务在队列中堆积最终导致OOM。最近工作中就遇到因为采用LinkedBlockingQueue作为阻塞队列,部分任务耗时80s+且不停有新任务进来,导致cpu和内存飙升服务器挂掉。

2.有界队列
常用的有两类,一类是遵循FIFO原则的队列如ArrayBlockingQueue与有界的LinkedBlockingQueue,另一类是优先级队列如PriorityBlockingQueue。PriorityBlockingQueue中的优先级由任务的Comparator决定。 
使用有界队列时队列大小需和线程池大小互相配合,线程池较小有界队列较大时可减少内存消耗,降低cpu使用率和上下文切换,但是可能会限制系统吞吐量。

3.同步移交

如果不希望任务在队列中等待而是希望将任务直接移交给工作线程,可使用SynchronousQueue作为等待队列。SynchronousQueue不是一个真正的队列,而是一种线程之间移交的机制。要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。只有在使用无界线程池或者有饱和策略时才建议使用该队列。

创建线程池的5种方式(jdk1.8之后加了一种)

public class Test {public static void main(String[] args) {//1.创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。ExecutorService cachedThreadPool = Executors.newCachedThreadPool();for (int i = 0; i < 10; i++) {int index = i;try {Thread.sleep(i * 500);} catch (InterruptedException e) {e.printStackTrace();}cachedThreadPool.execute(new Runnable() {@Overridepublic void run() {System.out.println("index: " + index);}});}//2.创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);for (int i = 0; i < 10; i++) {int index = i;fixedThreadPool.execute(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000);System.out.println("index: " + index);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}});}//3.创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();for (int i = 0; i < 10; i++) {final int index = i;singleThreadPool.execute(new Runnable() {@Overridepublic void run() {try {System.out.println(index);Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}});}//4.创建一个定长线程池,支持定时及周期性任务执行ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(0);// 第二个参数代表:初始延时多久// 第三个参数代表:周期的间隔// 第四个参数代表:时间的单位scheduledThreadPool.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {System.out.println("delay 1 seconds, and excute every 3 seconds");}}, 1, 3, TimeUnit.SECONDS);scheduledThreadPool.shutdown();//5.newWorkStealingPool创建一个拥有多个任务队列(以便减少连接数)的线程池//1.8的新特性ExecutorService newWorkStealingPool = Executors.newWorkStealingPool();List<Callable<String>> callables = Arrays.asList(callable("task1", 1),callable("task2", 2),callable("task3", 3));try {//单个执行线程String result = newWorkStealingPool.invokeAny(callables);System.out.println(result);//执行全部List<Future<String>> all = newWorkStealingPool.invokeAll(callables);//future是一个接口,有几个方法:boolean cancel(boolean mayInterruptIfRunning);//boolean isCancelled();//boolean isDone();//V get() throws InterruptedException, ExecutionException;//V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;for (Future<String> future : all) {String f = future.get();System.out.println(f);}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ExecutionException e) {// TODO Auto-generated catch blocke.printStackTrace();}}static Callable<String> callable(String result, long sleepSeconds) {    return () -> {        TimeUnit.SECONDS.sleep(sleepSeconds);        return result;    };}}




原创粉丝点击