欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

深入浅出线程池那些事

程序员文章站 2024-02-16 09:31:10
...

近期在复习线程池的面试题以及前两天面试一家公司实习生时被问到关于线程池。由于是第一次,所有舔着脸来和大家分享。如有不妥,还望指正。

1.什么是线程池?百度的解释如下:
深入浅出线程池那些事
线程池简单来说就是一个池子里面有一定量的线程,需要时从池子里拿,而不需要每次new 一个实例,增大开销。

2.使用线程池有什么好处
这里我分享几个链接,感觉写的很好,我要自己写无非也是照搬照抄(手动滑稽)。
https://www.jianshu.com/p/210eab345423
https://blog.csdn.net/u011394071/article/details/52893495
https://blog.csdn.net/leikun153/article/details/81609392

3.线程池重要的四个类
下面重点来了,学习Java,知其然和知其所以然也是有很大区别的,而使用线程池时非常简单的,无非new 一个ThreadPoolExecutor的实例。Java封装了ThreadPoolExecutor这个类,我们学习也应该深入源码去学习,真正做到知其然,知其所以然。
-ThreadPoolExecutor:
源码几百行,我截取其中几个重要的方法。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

   public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }
 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

以上是ThreadPoolExecutor的四个核心方法,里面的参数是维持一个线程池所必须的。这些方法我不一一解释,这个不是我要讲解的重点,也很容易理解。
-AbstractExecutorService
它是ThreadPoolExecutor的父类,是一个抽象类,实现了ExecutorService接口,它有以下核心方法。

 1.     public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
 2.     public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }
 3.     public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

很显然这是三个类似于重载的方法,submit()方法是向线程池提交线程任务的一个方法,与execute()方法类似,后面我们会说到execute()方法。
1. 第一个方法中传入了一个Runnable 类型的参数,返回值为一个Future对象,怎么理解这个Future对象呢,我们不谈线程池提交任务,就我们日常生活中,你把快递寄出去,然后你可以通过快递100等APP查询到他的物流去向,你可以知道结果如何。Future对象就扮演了这样一个角色,他不参与任务,通过这个对象的get()方法可以知道任务是否成功。
2. 第二个方法,传入了Runnable 类型的参数和一个泛型参数result,这个又是什么呢,这个result可以将线程中的一些结果(比如异常)保存起来,方便查看整个过程中发生了什么。
3. 第三个方法,传入了一个 Callable对象,这也说明sumbit()方法既可以传入Callable也可以传Runnable ,这是和execute()方法的一个区别。
-ExecutorService
这个接口定义了一些除了AbstractExecutorService中实现的方法,还有一些其他方法
深入浅出线程池那些事
submit是我们熟悉的。shutdown方法是可以关闭线程池的任务。

-Executor
它是最底层的一个接口, 它里面只定义了一个方法:
void execute(Runnable command);
}
这个方法向线程池提交任务。

3.sumbit()和execute()方法方法的区别
(1).execute提交的方式只能提交一个Runnable的对象,且该方法的返回值是void,也即是提交后如果线程运行后,和主线程就脱离了关系了,当然可以设置一些变量来获取到线程的运行结果。并且当线程的执行过程中抛出了异常通常来说主线程也无法获取到异常的信息的,只有通过ThreadFactory主动设置线程的异常处理类才能感知到提交的线程中的异常信息。
(2).submit提交的方式情况,上面我们已经详细说过,但从源码分析,我们可以看出他实际上是调用了execute()方法

深入浅出线程池那些事

4.ThreadPoolExecutor的重要参数
博主在面试过程中被问到,需要什么参数来维持一个线程池,一开始听到维持两个字我懵逼了。其实无非是说介绍一下线程池的核心参数。
这里我简要提一下。下一篇我将详细介绍ThreadPoolExecutor线程池参数设置技巧 。

  • corePoolSize:核心线程数 核心线程会一直存活,及时没有任务需要执行
    当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理

  • 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭

  • queueCapacity:任务队列容量(阻塞队列) 当核心线程数达到最大时,新任务会放在队列中排队等待执行

  • maxPoolSize:最大线程数 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
    当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常 keepAliveTime:线程空闲时间
    当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
    如果allowCoreThreadTimeout=true,则会直到线程数量=0

  • allowCoreThreadTimeout:允许核心线程超时
  • rejectedExecutionHandler:任务拒绝处理器

    如有错误,欢迎指正,小弟第一次写博客。

相关标签: Thread