Java线程池

故事扮演 提交于 2020-07-27 22:33:04

前言

Java中线程池属于jdk1.5版本加入的任务执行模块功能,任务执行是一个生产者-消费者模式,任务执行的顶层抽象是Executor接口,ExecutorService接口实现了Executor,线程池的抽象是ThreadPoolExecutor类,继承关系如下图:

Executors是一个工具类,内置了多种创建线程池的方法:

  • newFixedThreadPool:固定长度线程池
  • newCachedThreadPool :可缓存线程池
  • newSingleThreadExecutor:单线程的线程池
  • newScheduledThreadExecutor:固定长度的,可以延迟或定时执行的线程池

其内部实现不过是调用了ThreadPoolExecutor的不同构造方法,所以只要理解了ThreadPoolExecutor最全的构造方法的参数含义,自然就理解了上面列举的不同线程池的作用及区别。另外,在Alibaba编码规范中明确要求,线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样可以最大限度的保持灵活性,增加自己的线程名称,监控等,让其他人更好懂线程池的运行规则。所以我们以下只关注ThreadPoolExecutor类的用法。

ThreadPoolExecutor详解

public ThreadPoolExecutor(int corePoolSize, //线程池基本大小
                          int maximumPoolSize,//线程池最大大小
                          long keepAliveTime,//线程空闲存活时间
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,//阻塞队列
                          ThreadFactory threadFactory,//定制线程工厂
                          RejectedExecutionHandler handler//饱和策略) {}
  • 基本大小是即使没有任务,依然保持这个数量。
  • 最大数量也就是基础线程不够用的时候,创建新线程,最多创建这么多线程。
  • 空闲时间指如果超出基本数量的线程,空闲时间超过了配置时间就可以被回收。
  • 阻塞队列参数BlockingQueue<Runnable> workQueue,这是用来保存等待执行的任务,在构造方法中可以限制数量。
  • 线程工厂用来定制线程对象。
  • 不能无限制的创建线程,这样在糟糕的情况下会耗尽系统资源,通常是设置一个最大大小,那么问题来了,超过之后怎么办?交给饱和策略。

下面这行代码定义了一个默认10,最大100个线程,多余线程执行完成任务立刻回收,多余任务被保存在基于链表的,先进先出的阻塞队列中,最多保存200个任务,线程名称叫做mythread,队列中超过100个任务时打印一句log的线程池。

ThreadPoolExecutor executor=new ThreadPoolExecutor(10, 100, 0L,
        TimeUnit.SECONDS, new LinkedBlockingDeque<>(200), r -> {
            Thread thread = new Thread(r);
            thread.setName("mythread");
            return thread;
        }, (r, executor1) -> System.out.println("超过最大线程处理能力"));

饱和策略(拒绝策略)

饱和策略的抽象是接口RejectedExecutionHandler,只有一个方法void rejectedExecution(Runnable r, ThreadPoolExecutor executor);。我们可以实现自己的饱和策略,默认的4种饱和策略:

  • Abort:默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常。

  • Discard:直接抛弃,任务不执行。

  • DiscardOldest:抛弃最旧的,然后尝试提交新的任务。

  • Caller-Runs:调用者运行,将任务回退到调用者,也就是在主线程中执行。

Caller-Runs源码:

public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public CallerRunsPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}

线程工厂

这里有个ThreadFactory参数,ThreadFactory是干什么的呢?顾名思义,Factory在编程中不就是工厂模式么,通常用来构造一个对象。ThreadFactory接口里面只有一个方法:Thread newThread(Runnable r),目的就是定制化Thread对象。

Executors的默认ThreadFactory源码如下,定制了线程组,线程名称:

static class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    DefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                              Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                      poolNumber.getAndIncrement() +
                     "-thread-";
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                              namePrefix + threadNumber.getAndIncrement(),
                              0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

统计监控

ThreadPoolExecutor还有3个重要的方法,修饰符是protected,子类重写可以实现类似统计监控的功能,顾名思义,方法的含义为:在每个线程执行之前,每个线程执行之后和线程池完成关闭操作时执行。

protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }
protected void terminated() { }

一个示例

public class MyThreadPoolExecutor extends ThreadPoolExecutor {
    public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        System.out.println("线程" + Thread.currentThread().getId() + "执行结束");
    }

    @Override
    protected void terminated() {
        System.out.println("线程池关闭,释放资源");
    }

    public static void test() {
        MyThreadPoolExecutor myThreadPoolExecutor = new MyThreadPoolExecutor(10, 100, 0L,
                TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(100), r -> {
            Thread thread = new Thread(r);
            thread.setName("mythread");
            return thread;
        }, new ThreadPoolExecutor.AbortPolicy());

        for (int i = 0; i < 200; i++) {
            int num = i;
            myThreadPoolExecutor.execute(() -> {
                try {
                    //模拟耗时任务,不让线程快速执行完,导致任务积压,触发拒绝策略
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("任务" + num + "被执行");
            });
        }
        myThreadPoolExecutor.shutdown();
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!