Java线程池

本文对Executor、以及ThreadPoolExecutor中,可能用到的方法、参数做出介绍,文章参考了大量的博客以及API,最后总结得出,希望可以作为一本小字典,供大家翻阅。
文章内容过多,无法一一用简洁明了的话跟大家解释,如果有一些谬误,欢迎指出,我会及时纠正,有其他意见或者建议,也欢迎留言。结尾附上我的测试Demo,我会对各个线程池的作用进行测试。

Executors提供四种线程池

关于Executors的介绍,建议看完ThreadPoolExecutor再回来看吧,但是使用起来确实简单,Executors提供了四种定义好的线程池,分别为:

  1. newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
  2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。
  3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行,他有几个特殊的方法(也是执行runnable的方法,具有延迟执行的特点):

    • schedule()方法更注重保持间隔时间的稳定。
    • scheduleAtFixedRate()方法更注重保持执行频率的稳定,以固定的频率来执行某项计划(任务)。
    • scheduleWithFixedDealy,相对固定的延迟后,执行某项计划。
  4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

ThreadPoolExecutor

概述

  1. ThreadPoolExecutor作为java.util.concurrent包对外提供基础实现,它实现了Executor,
    ExecutorService接口,以内部线程池的形式对外提供管理任务执行,线程调度,线程池管理等等服务,详情可直接参考API;
  2. Executors提供的线程服务,通过参数设置来实现不同的线程池机制。
    此包中所定义的Executor、ExecutorService、ScheduledExecutorService、ThreadFactory
    和Callable 类的工厂和实用方法。此类支持以下各种方法:

    • 创建并返回设置有常用配置字符串的 ExecutorService的方法。
    • 创建并返回设置有常用配置字符串的 ScheduledExecutorService 的方法。
    • 创建并返回“包装的”ExecutorService 方法,它通过使特定于实现的方法不可访问来禁用重新配置。
    • 创建并返回ThreadFactory 的方法,它可将新创建的线程设置为已知的状态。
    • 创建并返回非闭包形式的 Callable的方法,这样可将其用于需要 Callable 的执行方法中。

Executor和ThreadPoolExecutor的关系

  1. Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,就是用来执行传进去的任务;
  2. 然后ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
  3. 抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;
  4. 而ThreadPoolExecutor继承自AbstractExecutorService。

ThreadPoolExecutor构造方法参数

  • corePoolSize - 池中所保存的线程数,包括空闲线程。
  • maximumPoolSize - 池中允许的最大线程数。
  • keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间(线程池中超过corePoolSize数目的空闲线程最大存活时间),可以allowCoreThreadTimeOut(true)使得核心线程也拥有有效时间。
  • unit - keepAliveTime 参数的时间单位。
  • workQueue - 执行前用于保持任务的队列。此队列仅保持由execute方法提交的 Runnable 任务。
  • threadFactory - 执行程序创建新线程时使用的工厂。
  • handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。

ThreadPoolExecutor可能抛出的异常

  • IllegalArgumentException - 如果 corePoolSize 或 keepAliveTime 小于 0,或者 maximumPoolSize 小于等于 0,或者 corePoolSize 大于 maximumPoolSize。
  • NullPointerException - 如果 workQueue、threadFactory 或 handler 为 null。

ThreadPoolExecutor构造方法参数之间的关系

一句话概括:小于max就能新建线程,大于corePoolSize就放入WorkQueue缓冲队列,超过最大处理能力就交给Handler处理,超过最大存活时间线程池就会关闭(超出corePoolSize部分,也可自定义),线程池分类不同,具体情况略有不同:
1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
6.当设置allowCoreThreadTimeOut(true)时,线程池中小于corePoolSize的线程空闲时间达到keepAliveTime也将关闭

unit可选的参数(构造方法参数)

多少时间自动回收线程?参数为java.util.concurrent.TimeUnit中的几个静态属性:

  • TimeUnit.DAYS; //天
  • TimeUnit.HOURS; //小时
  • TimeUnit.MINUTES; //分钟
  • TimeUnit.SECONDS; //秒
  • TimeUnit.MILLISECONDS; //毫秒
  • TimeUnit.MICROSECONDS; //微妙
  • TimeUnit.NANOSECONDS; //纳秒

阻塞队列(构造方法参数)

一次写了太多事务,线程池处理不过来,先装起来。 workQueue指的是阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:

  1. PriorityBlockingQueue:类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序.
  2. ArrayBlockingQueue: 规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的.
  3. LinkedBlockingQueue: 基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
  4. SynchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

其中LinkedBlockingQueue和ArrayBlockingQueue比较起来,它们背后所用的数据结构不一样,导致LinkedBlockingQueue的数据吞吐量要大于ArrayBlockingQueue,但在线程数量很大时其性能的可预见性低于ArrayBlockingQueue.

拒绝策略(构造方法参数)

handler:事务太多,是拒绝?还是接受?表示当拒绝处理任务时的策略,有以下四种取值:

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

几个比较实用的方法:

线程执行

  • execute() - 在将来某个时间执行给定任务。可以在新线程中或者在现有池线程中执行该任务。如果无法将任务提交执行,或者因为此执行程序已关闭,或者因为已达到其容量,则该任务由当前
    RejectedExecutionHandler 处理。
  • submit() - 提交一个事务(参数可以是:runnable对象,回调接口,或者是与泛型相同的对象),它能够返回任务执行的结果,

线程关闭

  • shutdown() - 按过去执行已提交任务的顺序发起一个有序的关闭,但是不接受新任务。如果已经关闭,则调用没有其他作用。
  • shutdownNow() - 尝试停止所有的活动执行任务、暂停等待任务的处理,并返回等待执行的任务列表。在从此方法返回的任务队列中排空(移除)这些任务。
    并不保证能够停止正在处理的活动执行任务,但是会尽力尝试。 此实现通过 Thread.interrupt()
    取消任务,所以无法响应中断的任何任务可能永远无法终止。

其它

  • purge() - 尝试从工作队列移除所有已取消的Future任务。此方法可用作存储回收操作,它对功能没有任何影响。取消的任务不会再次执行,但是它们可能在工作队列中累积,直到worker线程主动将其移除。调用此方法将试图立即移除它们。但是,如果出现其他线程的干预,那么此方法移除任务将失败。
  • getQueue - 返回此执行程序使用的任务队列。对任务队列的访问主要用于调试和监控。此队列可能正处于活动使用状态中。获取任务队列不妨碍已加入队列的任务的执行。
  • remove - 从执行程序的内部队列中移除此任务(如果存在),从而如果尚未开始,则其不再运行。

几个供子类重写的方法:

  1. beforeExecute() - 在执行给定线程中的给定 Runnable 之前调用的方法。此方法由将执行任务 r 的线程 t 调用,并且可用于重新初始化 ThreadLocals 或者执行日志记录。
    此实现不执行任何操作,但可在子类中定制。注:为了正确嵌套多个重写操作,此方法结束时,子类通常应该调用
    super.beforeExecute。
  2. afterExecute() - 基于完成执行给定 Runnable 所调用的方法。此方法由执行任务的线程调用。如果非 null,则 Throwable 是导致执行突然终止的未捕获 RuntimeException 或 Error。
    注:当操作显示地或者通过submit 之类的方法包含在任务内时(如FutureTask),这些任务对象捕获和维护计算异常,因此它们不会导致突然终止,内部异常不会 传递给此方法。
    此实现不执行任何操作,但可在子类中定制。
    注:为了正确嵌套多个重写操作,此方法开始时,子类通常应该调用
    super.afterExecute。
  3. terminated() - 当 Executor 已经终止时调用的方法。默认实现不执行任何操作。
    注:为了正确嵌套多个重写操作,子类通常应该在此方法中调用super.afterExecute。
  4. finalize() - protected void finalize()当不再引用此执行程序时,调用 shutdown。

ThreadPoolExecutor类中其他的一些比较重要成员变量

  • private final BlockingQueue[Runnable] workQueue;
    //任务缓存队列,用来存放等待执行的任务
  • private final ReentrantLock mainLock = new ReentrantLock();
    //线程池的主要状态锁,对线程池状态(比如线程池大小、runState等)的改变都要使用这个锁
  • private final HashSet workers = new HashSet);
    //用来存放工作集
  • private volatile long keepAliveTime; //线程存货时间
  • private volatile boolean allowCoreThreadTimeOut;
    //是否允许为核心线程设置存活时间
  • private volatile int corePoolSize;
    //核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)
  • private volatile int maximumPoolSize; //线程池最大能容忍的线程数
  • private volatile int poolSize; //线程池中当前的线程数
  • private volatile RejectedExecutionHandler handler; //任务拒绝策略
  • private volatile ThreadFactory threadFactory; //线程工厂,用来创建线程
  • private int largestPoolSize; //用来记录线程池中曾经出现过的最大线程数
  • private long completedTaskCount; //用来记录已经执行完毕的任务个数

前人经验:

  1. 用ThreadPoolExecutor自定义线程池,看线程是的用途,如果任务量不大,可以用无界队列,如果任务量非常大,要用有界队列,防止OOM
  2. 如果任务量很大,还要求每个任务都处理成功,要对提交的任务进行阻塞提交,重写拒绝机制,改为阻塞提交。保证不抛弃一个任务
  3. 最大线程数一般设为2N+1最好,N是CPU核数
  4. 核心线程数,看应用,如果是任务,一天跑一次,设置为0,合适,因为跑完就停掉了,如果是常用线程池,看任务量,是保留一个核心还是几个核心线程数
  5. 如果要获取任务执行结果,用CompletionService,但是注意,获取任务的结果的要重新开一个线程获取,如果在主线程获取,就要等任务都提交后才获取,就会阻塞大量任务结果,队列过大OOM,所以最好异步开个线程获取结果

一种个人比较喜欢的关闭线程的方式

class MyRunnable implements Runnable{
    private boolean stop;
    private Thread thread;

    public MyRunnable() {
        stop = false;
        thread = Thread.currentThread();
    }

    @Override
    public void run() {
        int count = 0;
        while (!stop) {
            try {
                System.out.println(count++);
                Thread.sleep(200);
            } catch (InterruptedException e) {
                this.thread.interrupt();
            }
        }
    }

    public void stopThread() {
        this.stop = !stop;
    }
}

public class SafelyStop {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myRunnable.stopThread();
    }
}

源码:

package com;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Test {
    /** * 容量大小固定的线程池:因为线程池大小限制为4,所以一次输出4个数字,并且会发现线程被复用了 */
    public static void testFixedThreadPool() {
        for (int i = 0; i < 10; i++) {
            final int index = i;
            ThreadUtil.getFixedThreadPool().execute(new Runnable() {

                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName() + ":" + index);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    /** * 带延迟效果的线程池:线程延迟了3秒执行,容量大小,而且线程只新建了3个 */
    public static void testScheduledThreadPool1() {
        for (int i = 0; i < 10; i++) {
            ThreadUtil.getScheduledThreadPool().schedule(new Runnable() {

                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + ":" + "delay 3 seconds");
                }
            }, 3, TimeUnit.SECONDS);
        }
    }

    /** * 带延迟效果的线程池:线程延迟了1秒执行,之后每3秒执行一次,而且线程是3个中的随机一个,以固定的周期执行 */
    public static void testScheduledThreadPool2() {
        ThreadUtil.getScheduledThreadPool().scheduleWithFixedDelay(new Runnable() {

            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + ":" + "delay 3 seconds");
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 1, 2, TimeUnit.SECONDS);
    }

    /** * 带延迟效果的线程池:线程延迟了1秒执行,之后每3秒执行一次,而且线程是3个中的随机一个,执行完任务,以固定的周期进行延迟 */
    public static void testScheduledThreadPool3() {
        ThreadUtil.getScheduledThreadPool().scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + ":" + "delay 3 seconds");
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 1, 2, TimeUnit.SECONDS);
    }

    /** * 可自动回收线程的线程池:Java默认60秒自动回收,测试回收功能 */
    public static void testCachedThreadPool() {
        for (int i = 0; i < 10; i++) {
            final int index = i;
            ThreadUtil.getCachedThreadPool().execute(new Runnable() {

                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + ":" + index);

                }
            });
            try {
                Thread.sleep(70000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /** * 这是一个单例线程,可以看到:所有的事务使用同一个线程 */
    public static void testSingleThreadExecutor() {
        for (int i = 0; i < 10; i++) {
            final int index = i;
            ThreadUtil.getSingleThreadExecutor().execute(new Runnable() {

                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + ":" + index);
                }
            });
        }
    }

    /** * 自定义线程池测试 */
    public static void testThreadPoolExecutor() {
        for (int i = 1; i < 20; i++) {
            System.out.println("提交第" + i + "个任务!");
            ThreadUtil.getThreadpool().execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "running=====");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "finish=====");
                }
            });
        }
    }

    public static void main(String[] args) throws Exception {
        // testFixedThreadPool();
        // testCachedThreadPool();
        // testScheduledThreadPool2();
        testSingleThreadExecutor();
    }
}

线程工具类

包含了以上提到的全部线程


class ThreadUtil {
    private static ThreadPoolExecutor threadPool;
    private static ExecutorService cachedThreadPool;
    private static ExecutorService fixedThreadPool;
    private static ScheduledExecutorService scheduledThreadPool;
    private static ExecutorService singleThreadExecutor;

    /** * 个人比较喜欢的一种单例模式写法,不知道有没有人这么写 * * @return 线程池 */
    public static ThreadPoolExecutor getThreadpool() {
        if (threadPool == null)
            createThreadPool();
        return threadPool;
    }

    public static ExecutorService getCachedThreadPool() {
        if (cachedThreadPool == null)
            crateCachedThreadPool();
        return cachedThreadPool;

    }

    public static ExecutorService getFixedThreadPool() {
        if (fixedThreadPool == null)
            crateFixedThreadPool();
        return fixedThreadPool;

    }

    public static ScheduledExecutorService getScheduledThreadPool() {
        if (scheduledThreadPool == null)
            crateScheduledThreadPool();
        return scheduledThreadPool;
    }

    public static ExecutorService getSingleThreadExecutor() {
        if (singleThreadExecutor == null)
            crateSingleThreadExecutor();
        return singleThreadExecutor;
    }

    /** * 单例线程 */
    private static void crateSingleThreadExecutor() {
        if (singleThreadExecutor == null)
            singleThreadExecutor = Executors.newSingleThreadExecutor(new CustomThreadFactory());
    }

    /** * 创建一个定长线程池,支持定时及周期性任务执行。 */
    private static void crateScheduledThreadPool() {
        if (scheduledThreadPool == null)
            scheduledThreadPool = Executors.newScheduledThreadPool(3, new CustomThreadFactory());
    }

    /** * 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 */
    private static void crateFixedThreadPool() {
        if (fixedThreadPool == null)
            fixedThreadPool = Executors.newFixedThreadPool(4, new CustomThreadFactory());
    }

    /** * 带缓存的队列,如果有新的事务,自动使用之前的线程,线程会自动回收 */
    private static void crateCachedThreadPool() {
        if (cachedThreadPool == null)
            cachedThreadPool = Executors.newCachedThreadPool(new CustomThreadFactory());
    }

    /** * 自定义线程池 */
    private synchronized static void createThreadPool() {
        if (threadPool == null)
            threadPool = new ThreadPoolExecutor(
                    3, // 线程数量
                    5, // 线程池最大值
                    2, // 最大存活时间
                    TimeUnit.SECONDS, // 按照秒计算
                    new ArrayBlockingQueue<Runnable>(5), // 缓冲池(阻塞队列)
                    new CustomThreadFactory(), // 线程工厂
                    new CustomRejectedExecutionHandler()// 超出最大处理能力处理机制(拒绝策略)
            );
    }

    /** * 关闭所有的线程 */
    public static void destory() {
        if (!threadPool.isShutdown()) {
            threadPool.shutdownNow();
        }
    }

    /** * 自定义线程工厂,这里给每一个线程取了一个名字 * * @author ChenSS * */
    private static class CustomThreadFactory implements ThreadFactory {

        private AtomicInteger count = new AtomicInteger(0);

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            String threadName = ThreadFactory.class.getSimpleName() + count.addAndGet(1);
            thread.setName(threadName);
            return thread;
        }
    }

    /** * 自定义处理策略 * * @author ChenSS * */
    private static class CustomRejectedExecutionHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable runnable, ThreadPoolExecutor executor) {
            try {
                // 超出最大线程池极限的时候的处理机制,这里也可以直接拒绝、或者说抛出异常,而这里是等待
                executor.getQueue().put(runnable);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

扩展:添加了简单的暂停/恢复功能的子类

/** * 下面是一个添加了简单的暂停/恢复功能的子类: * * @author ChenSS * */
class PausableThreadPoolExecutor extends ThreadPoolExecutor {
    private boolean isPaused;
    private ReentrantLock pauseLock = new ReentrantLock();
    private Condition unpaused = pauseLock.newCondition();

    public PausableThreadPoolExecutor(……){
        //构造方法,需要哪个自己选择super(......)
    }

    protected void beforeExecute(Thread t, Runnable r) {
        pauseLock.lock();
        try {
            while (isPaused)
                unpaused.await();
        } catch (InterruptedException ie) {
            t.interrupt();
        } finally {
            pauseLock.unlock();
        }
        super.beforeExecute(t, r);
    }

    public void pause() {
        pauseLock.lock();
        try {
            isPaused = true;
        } finally {
            pauseLock.unlock();
        }
    }

    public void resume() {
        pauseLock.lock();
        try {
            isPaused = false;
            unpaused.signalAll();
        } finally {
            pauseLock.unlock();
        }
    }

    /** * 当不再引用此执行程序时,调用 shutdown。 */
    @Override
    protected void finalize() {
        //关闭前做什么?功能自己扩展……
        super.finalize();
    }
}

你可能感兴趣的:(ThreadPool,线程,线程池,executor)