线程池回顾复习

线程池的作用

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 可有效控制最大并发线程数,(线程并发数量过多,抢占系统资源从而导致阻塞)
  3. 提高线程的可管理性。使用线程池可以进行统一的分配,调优和监控。

线程池的启动策略

  1. 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也
    不会马上执行它们。
  2. 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
    • 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
    • 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
    • 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;
    • 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。
  3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
  4. 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

举例说明:
当线程数小于corePoolSize时,每添加一个任务,则立即开启线程执行
当corePoolSize满的时候,后面添加的任务将放入缓冲队列workQueue等待;
当workQueue也满的时候,看是否超过maximumPoolSize线程数,如果超过,默认拒绝执行

假如:corePoolSize=2,maximumPoolSize=3,workQueue容量为8;
最开始,执行的任务A,B,此时corePoolSize已用完,再次执行任务C,则
C将被放入缓冲队列workQueue中等待着,如果后来又添加了7个任务,此时workQueue已满,
则后面再来的任务将会和maximumPoolSize比较,由于maximumPoolSize为3,所以只能容纳1个了,
因为有2个在corePoolSize中运行了,所以后面来的任务默认都会被拒绝。

线程池ThreadPoolExecutor的构造

  • int corePoolSize => 该线程池中核心线程数最大值
核心线程:
核心线程数,能够同时执行的任务数量
线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corePoolSize,则新建的是非核心线程
核心线程默认情况下会一直存活在线程池中,即使这个核心线程啥也不干(闲置状态)。
如果指定ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true,那么核心线程如果不干活(闲置状态)的话,超过一定时间(时长下面参数决定),就会被销毁掉
很好理解吧,正常情况下你不干活我也养你,因为我总有用到你的时候,但有时候特殊情况(比如我自己都养不起了),那你不干活我就要把你干掉了
  • int maximumPoolSize
除去缓冲队列中等待的任务,最大能容纳的任务数(其实是包括了核心线程池数量)
  • long keepAliveTime
超出workQueue的等待任务的存活时间,就是指maximumPoolSize里面的等待任务的存活时间
该线程池中非核心线程闲置超时时长
一个非核心线程,如果不干活(闲置状态)的时长超过这个参数所设定的时长,就会被销毁掉
如果设置allowCoreThreadTimeOut = true,则会作用于核心线程
  • TimeUnit unit
keepAliveTime的单位,TimeUnit是一个枚举类型,其包括:
NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MICROSECONDS : 1微秒 = 1毫秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小时
DAYS : 天
  • BlockingQueue workQueue
该线程池中的任务队列:维护着等待执行的Runnable对象
当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务
常用的workQueue类型:
SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大
LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize
ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误
DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务
  • threadFactory
创建线程的工厂,使用系统默认的类
  • handler
当任务数超过maximumPoolSize时,对任务的处理策略,默认策略是拒绝添加

线程池创建示例

public class ThreadPoolManager {
    private static ThreadPoolManager mIntance = new ThreadPoolManager();

    public static ThreadPoolManager getInstance() {
        return mIntance;
    }

    private int corePoolSize;//核心线程池的数量,能够同时执行的线程数量
    private int maximumPoolSize;//最大线程池的数量
    private long keepAliveTime = 1;//存活时间
    private TimeUnit unit = TimeUnit.HOURS;//时间单位
    private ThreadPoolExecutor executor;

    private ThreadPoolManager() {
        //对corePoolSize进行赋值,算法:当前设备的可用处理器核心数*2 + 1,能够让CPu的效率得到最大程度的发挥
        corePoolSize = Runtime.getRuntime().availableProcessors() * 2 + 1;
        maximumPoolSize = corePoolSize;//虽然用不到,但是如果为0则会报错
        //线程池的机制:领工资机制
        executor = new ThreadPoolExecutor(
                corePoolSize, //3
                maximumPoolSize,//5,当缓冲队列也满的时候会放入最大线程池,但是它是包含了核心线程池数量的
                keepAliveTime,
                unit,
                new LinkedBlockingQueue<Runnable>(), //超出核心线程池的任务将会放入缓冲队列等待着
                Executors.defaultThreadFactory(), //创建线程的工厂
                new ThreadPoolExecutor.AbortPolicy() //当最大线程池满的时候的处理策略,默认策略是拒绝添加执行
        );
    }

    /**
     * 往线程池中添加任务
     *
     * @param runnable
     */
    public void execute(Runnable runnable) {
        if (runnable == null) return;

        executor.execute(runnable);
    }

    /**
     * 从线程池中移除任务
     *
     * @param runnable
     */
    public void remove(Runnable runnable) {
        if (runnable == null) return;

        executor.remove(runnable);
    }
}

常见的四种线程池

  • CachedThreadPool()
    可缓存线程池:它的特点
    • 线程数无限制
    • 有空闲线程则复用空闲线程,若无空闲线程则新建线程
    • 一定程序减少频繁创建/销毁线程,减少系统开销

创建方法ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
源码:

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }
  • FixedThreadPool()
    定长线程池:它的特点
    • 可控制线程最大并发数(同时执行的线程数)
    • 超出的线程会在队列中等待.
      创建方法
//nThreads => 最大线程数即maximumPoolSize
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads);

源码:

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }
  • ScheduledThreadPool()
    定时线程池:它的特点
    • 支持定时及周期性任务执行。

创建方法:

//nThreads => 最大线程数即maximumPoolSize
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);

源码:

private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue(), threadFactory);
    }
  • SingleThreadExecutor()
    单线程化的线程池:它的特点
    • 有且仅有一个工作线程执行任务
    • 所有任务按照指定顺序执行,即遵循队列的入队出队规则
    • 串行执行所有的线程

创建方法:ExecutorService singleThreadPool = Executors.newSingleThreadPool();
源码:

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值