关于线程池最基本的东西

最近用到多线程了,特意翻看一些关于多线程、线程池的文章,因为平常用的不是很多所以对这方面不是特别的了解,只是简单会使用并不清楚原理什么的。今天我就总结下我这几天看的一些东西,深层次的就不写啦,也没有时间去仔细查看,只总结一些日常使用所需要了解的要点。

一、创建线程池需要哪些参数

先来说说ThreadPoolExecutor源码,查看创建线程池的构造函数:

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

不管你调用的是ThreadPoolExecutor的哪个构造函数,最终都会执行到这个构造函数的,这个构造函数有7个参数,正是由于对这7个参数值的赋值不同,造成生成不同类型的线程池,比如我们常见的CachedThreadPoolExecutor、FixedThreadPoolExecutor SingleThreadPoolExecutor、ScheduledThreadPoolExecutor,看看这几个参数的具体含义:

   1、corePoolSize:线程池中核心线程的数量;当提交一个任务到线程池的时候,线程池会创建一个线程来执行执行任务,即使有其他空闲的线程存在,直到线程数达到corePoolSize时不再创建,这时候会把提交的新任务放入到阻塞队列中,如果调用了线程池的preStartAllCoreThreads方法,则会在创建线程池的时候初始化出来核心线程;    

2、maximumPoolSize:线程池允许创建的最大线程数;如果阻塞队列已经满了,同时已经创建的线程数小于最大线程数的话,那么会创建新的线程来处理阻塞队列中的任务;    

3、keepAliveTime:线程活动保持时间,指的是工作线程空闲之后继续存活的时间;    

4、unit:参数keepAliveTime的时间单位;    

5、workQueue:阻塞队列;用于存储等待执行的任务,有四种阻塞队列类型,ArrayBlockingQueue(基于数组的有界阻塞队列)、LinkedBlockingQueue(基于链表结构的阻塞队列)、SynchronousQueue(不存储元素的阻塞队列)、PriorityBlockingQueue(具有优先级的阻塞队列),ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。

6、threadFactory:用于创建线程的线程工厂;

7、handler:当阻塞队列满了,且没有空闲线程的情况下,线程池中的线程数目已经达到了最大线程数量,处于饱和状态,那么必须采取一种策略来处理新提交的任务,我们可以自己定义处理策略,也可以使用系统已经提供给我们的策略,先来看看系统为我们提供的4种策略,AbortPolicy(直接抛出异常)、CallerRunsPolicy(只有调用者所在的线程来运行任务)、DiscardOldestPolicy(丢弃阻塞队列中最近的一个任务,并执行当前任务)、Discard(直接丢弃),稍后详细分析;

二、线程池的状态

在ThreadPoolExecutor中定义了一个volatile变量,另外定义了几个static final变量表示线程池的各个状态:

volatile int runState;

static final int RUNNING = 0;

static final int SHUTDOWN = 1;

static final int STOP = 2;

static final int TERMINATED = 3;

runState表示当前线程池的状态,它是一个volatile变量用来保证线程之间的可见性;  

当创建线程池后,初始时,线程池处于RUNNING状态;  

如果调用了shutdown()方法,处于SHUTDOWN状态,此时线程池不接受新的任务,它会等待所有任务执行完毕;

如果调用了shutdownNow()方法,处于STOP状态,此时线程池不接受新的任务,并且会去尝试终止正在执行的任务;  

当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。

三、线程池队列满后的拒绝策略

handler:表示当拒绝处理任务时的策略,有以下四种取值:

AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 默认。 

DiscardPolicy:也是丢弃任务,但是不抛出异常。  

DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

CallerRunsPolicy:由调用线程处理该任务(当前运行的线程去处理)

四、常见的五种线程池(可直接使用)

1、newCachedThreadPool:缓存线程池,该类线程池中线程的数量是不确定的,理论上可以达到Integer.MAX_VALUE个,这种线程池中的线程都是非核心线程,既然是非核心线程,那么就存在超时淘汰机制了,当里面的某个线程空闲时间超过了设定的超时时间的话,就会回收掉该线程;

   public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

2、FixedThreadPool:固定线程池,这类线程池中是只存在核心线程的,对于核心线程来说,如果我们不设置allowCoreThreadTimeOut属性的话是不存在超时淘汰机制的,这类线程池中的corePoolSize的大小是等于maximumPoolSize大小的,也就是说,如果线程池中的线程都处于活动状态的话,如果有新任务到来,他是不会开辟新的工作线程来处理这些任务的,只能将这些任务放到阻塞队列里面进行等到,直到有核心线程空闲为止;

    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

3、ScheduledThreadPool:任务线程池,这种线程池中核心线程的数量是固定的,而对于非核心线程的数量是不限制的,同时对于非核心线程是存在超时淘汰机制的,主要适用于执行定时任务或者周期性任务的场景;

 public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }

4、SingleThreadPool:单一线程池,线程池里面只有一个线程,同时也不存在非核心线程,感觉像是FixedThreadPool的特殊版本,他主要用于确保任务在同一线程中的顺序执行,有点类似于进行同步吧;

    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

5、newWorkStealingPool 是jdk1.8才有的,会根据所需的并行层次来动态创建和关闭线程,通过使用多个队列减少竞争,底层用的 ForkJoinPool来实现的。ForkJoinPool 的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。

    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,null, true);
    }

五、线程池的使用

1、线程池中的线程初始化  

默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程。   在实际中如果需要线程池创建之后立即创建线程,可以通过以下两个方法办到:  

prestartCoreThread():初始化一个核心线程;

prestartAllCoreThreads():初始化所有核心线程

2、线程池运行的4个阶段:

(1):poolSize < corePoolSize,则直接创建新的线程(核心线程)来执行当前提交的任务;    

(2):poolSize = corePoolSize,并且此时阻塞队列没有满,那么会将当前任务添加到阻塞队列中,如果此时存在工作线程(非核心线程)的话,那么会由工作线程来处理该阻塞队列中的任务,如果此时工作线程数量为0的话,那么会创建一个工作线程(非核心线程)出来;    

(3):poolSize = corePoolSize,并且此时阻塞队列已经满了,那么会直接创建新的工作线程(非核心线程)来处理阻塞队列中的任务;    

(4):poolSize = maximumPoolSize,并且此时阻塞队列也满了的话,那么会触发拒绝机制,具体决绝策略采用的是什么就要看我们创建ThreadPoolExecutor的时候传入的RejectExecutionHandler参数了;

来一张流程图比较清晰直观:

3、线程池的关闭的方法 

shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但不会接受新的任务

shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

4、任务缓存队列及排队策略  任务缓存队列,即workQueue,它用来存放等待执行的任务。 

ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;  

LinkedBlockingQueue基于链表的先进先出队列,没有指定此队列大小,默认为Integer.MAX_VALUE;  

synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是直接新建一个线程来执行新来的任务。

5、线程池几个重要的方法:

execute():通过这个方法可以向线程池提交一个任务,交由线程池去执行,没有返回值

submit():它能够返回任务执行的结果,它实际上还是调用的execute()方法,利用了Future来获取任务执行结果

getQueue() :获取线程池中任务队列等待的长度

getPoolSize() :获取线程池的数量

getActiveCount():获取池中活动线程的数量

getCompletedTaskCount():获取完成的线程数量


先就写这么多吧,大概都是一些线程常识基本知识吧。其实还有很多东西没写出来,只是轻描淡写了几下之后抽时间读读源码分析在写深层次的。如有问题大家可留言一起探讨交流。

参考:https://blog.csdn.net/gol_phing/article/details/49032055

          https://blog.csdn.net/hzw19920329/article/details/52372348

目录

一、创建线程池需要哪些参数

二、线程池的状态

三、线程池队列满后的拒绝策略

四、常见的五种线程池(可直接使用)

五、线程池的使用


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值