Java线程池学习

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tangyaya8/article/details/81592660

背景

再现代的操作系统中,进程是资源分配的最小单位,而线程是处理器调度的最小单位,而再Java语言中,最小调度代码的单位也是线程(Thread),在越来越多的高并发,要求实时性高的系统中多线程对对单线程来说有着很大的优势.

为什么要用线程池

在Java中,我们在开启一个线程并运行的时候,会这样写 new Thread(() ->{}).start();,
如果要开启多个线程,那么我们要这样写:

        new Thread(() ->{});
        new Thread(() ->{});
        new Thread(() ->{});
        new Thread(() ->{});
        .....

这样的写法不但又代码冗余,不美观之外,还会带来其他问题:
首先,我们来计算一个线程运行的时间:
开启线程所用时间(T1)+
线程正真执行代码所用的时间(T2)+
关闭线程所用过的时间(T3)
问题:
1:如果T1+T3>T2,我们要花比线程执行时间还多的时间用来开启,关闭线程
2.如果线程不够还要重写代码开启更多的线程
3.如果开启的线程过多,无法回收,空着占用系统资源

线程池

线程池,数据库连接池等 “池”,都是提前把资源准备好,等到用的时候直接用,不用再创建,用完之后,再放回”池”子里面,接下来,对照着源码来学习Java中的线程池

Executor

这是java1.5的时候新加入的一个接口,是大名鼎鼎的Doug Lea写的,这个接口中只定义了一个方法,入参是Runnable的一个实例,其实就是执行run()方法

/*
 * @since 1.5
 * @author Doug Lea
 */
public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

ExecutorService

解释几个重要的方法
此方法不等待先前提交的任务完成执行,也就是直接会停止当前提交的任务
void shutdown();

此方法会会立马终止当前执行的线程,返回没有执行的任务列表
List<Runnable> shutdownNow();

等待当前所有任务执行完,或者没有在才规定的时间内执行完,会停止
boolean awaitTermination(long timeout, TimeUnit unit)

此方法会传入一个回调类型,在提交任务之后,会异步返回结果
<T> Future<T> submit(Callable<T> task);

ThreadPoolExecutor

这个类是ExecutorService默认的实现类

线程池状态:

RUNNING:接受新的task,并且执行队列中已有的task
SHUTDOWN:不再接受新的task,但是执行完队列中已又的task
STOP:不再接受新的task,并且不再执行队列中的task,并且中断正在执行的task
TIDYING :所有任务都已终止,workerCount为零, 线程转换到状态TIDYING,将运行terminate()回调方法
TERMINATED:terminated()方法执行完毕,将状态改为TERMINATED
以下为状态互相转换的几种情况

    RUNNING -> SHUTDOWN
         On invocation of shutdown(), perhaps implicitly in finalize()
      (RUNNING or SHUTDOWN) -> STOP
        On invocation of shutdownNow()
      SHUTDOWN -> TIDYING
         When both queue and pool are empty
      STOP -> TIDYING
         When pool is empty
      TIDYING -> TERMINATED
         When the terminated() hook method has completed

其他重要属性

用来存放工作集:
private final BlockingQueue<Runnable> workQueue;
线程池主要状态,等改变的时候都要用的可重入锁
private final ReentrantLock mainLock = new ReentrantLock();
线程池中工作集,只有在得到锁的时候,才可以更改
private final HashSet<Worker> workers = new HashSet<>();

执行execute()方法遵循的三个步骤

1.如果当前线程数量小于corePoolSize则新建线程,用来执行task
2.如果当前线程大于corePoolSize,则将它加入等待队列,等待被执行, 如果添加失败,则新建线程去执行
3.如果当前线程数量大于maximumPoolSize,则执行拒绝策略

ThreadPoolExecutor线程池拒绝策略:

1.ThreadPoolExecutor.AbortPolicy:当线程数量大于maximumPoolSize时候,拒绝接收task,并抛出一个RejectedExecutionException异常
2. DiscardPolicy:当线程数量大于maximumPoolSize时候,拒绝接受task,并静默丢弃task
3. DiscardOldestPolicy:丢弃最久未使用的task,接受新的task
4. CallerRunsPolicy:不加入队列,由调用线程处理该任务

任务缓存队列的设置策略

1.ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
2.LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
3.SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
4.PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

ThreadPoolExecutor构造器参数解释:

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

int corePoolSize:顾名思义,线程池中同时执行几个线程的大小
int maximumPoolSize:线程池最大的大小
long keepAliveTime:当线程池中的线程数大于corePoolSize的时候,开始执行timeUnit后在线程消亡前最大存活时间
ThreadFactory threadFactory:用线程工厂生产
RejectedExecutionHandler handler:如果缓冲队列满了,拒绝策略

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页