技术自查第五篇:线程进阶(二)篇

5 篇文章 0 订阅
5 篇文章 0 订阅

前言

线程的基础知识以及原理,我们都已经学习了一大部分,线程的确会为我们带来好处,但相对应我们需要付出代价,系统资源换取。当大量的线程创建和销毁,无疑消耗大量的资源。

为了应付这种情况,Java就推出线程池。

线程池

为了降低资源的消耗,提高响应速度,使线程可重复使用。

作用

1. 降低资源消耗,线程其实也是一个对象,既然是对象,那就涉及到堆内存,GC回收等等

2. 提高响应速度,通过从线程池获取比创建相乘,无疑是前者速度快

3. 线程可重复使用,从线程池获取线程且使用完毕后,可放回线程池等待下次使用

说完概念,我们配合源码了解线程池

ThreadPoolExcutor.class

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;
}

从源码可知,创建线程池需要七个参数

七个参数

1. corePoolSize:核心线程数量,线程池创建完毕后,并不是马上创建核心线程,而是接到任务后才创建

2. maximumPoolSize:最大线程数,最大线程数 = 核心线程数 + 非核心线程数

3. keepAliceTime:非核心线程最大空闲/存活时间

4. TimeUnit:时间单位

        NANOSECONDS : 1微毫秒 = 1微秒 / 1000

        MICROSECONDS : 1微秒 = 1毫秒 / 1000

        MILLISECONDS : 1毫秒 = 1秒 /1000

        SECONDS : 秒

        MINUTES : 分

        HOURS : 小时

5. BlockingQueue<Runnable> workQueue:阻塞队列/等待任务执行队列

        当核心线程都被占用时,就会把任务存放到该队列中,等待被执行,总共有四种队列类型

        SynchronousQueue:该队列并不会存放任务,而是接到任务后,马上通知线程池创建非核心线程来执行任务,故为了不让最大线程数出现爆满异常,maximumPoolSize一般被设置成Integer.MAX

        LinkedBlockingQueue: 该队列接收到任务后,就会存放该任务到队列中,由于该队列没有最大数量限制,故maximumPoolSize等于无效

        ArrayBlockingQueue:该队列接收到任务后,就会存放该任务到队列中,如果队列满时,就会创建非核心线程,但如果达到最大线程数时,就会根据不同饱和策略,做出不同的处理

        DelayQueue:该队列的元素必须实现Delayed接口,进入该队列的任务,是达到指定的时间才会执行任务

队列是否有最大值

maximumPoolSize

备注

SynchronousQueue

0

有效,但一般设置为:Integer.MAX

由于一旦该队列接收到任务,就会马上交回线程池处理,所以为了防止出现最大线程数异常,所以设置为Integer.MAX

LinkedBlockingQueue

无效

由于核心线程数满时,就会存入该队列中,但该队列没有最大值,所以变相等于maximumPoolSize无效

ArrayBlockingQueue

自定义

由于该队列有最大值,当队列满时,就会创建非核心线程数,当线程数达到最大值时,就会根据饱和政策做出对应的处理

DelayQueue

自定义

6. threadFactory:顾名思义,创建线程的工厂

                可以使用线程工厂给每个创建的线程自定义名字,一般情况下无序设置该参数

7. RejectedExecutionHandler handler:饱和策略

        当线程池出现异常时,不同的处理方式/机制

        AboardPolicy:线程池的默认值,抛出异常

        CallerRunsPolicy:线程池里的线程都被占用时,就会调用该线程池的调用者,来处理该任务

        DiscaradPolicy:抛弃该任务

        DiscaradOldPolicy:表示抛弃最近的任务,并执行当前任务

核心线程,非核心线程,阻塞队列,饱和策略这些概念和名词,第一次听到都会感觉难以理解,打个比喻的话

比喻

  • 线程任务=需求
  • 核心线程=公司正式员工
  • 非核心线程=公司外包员工
  • 最大线程数=公司正式员工+公司外包员工
  • 队列=需求池
  • 饱和政策=需求池>(公司正式员工+公司外包员工)时的,应急方案

 

1. 当产品经理提出需求时,查看正式员工是否有空闲,有就直接交给他处理,没有则进入第二步

2. 查看需求池是否已满,没有则把新需求放入需求池中,等待正式员工处理,如果满了,则进入第三部

3. 当正式员工和需求池都已经满时,则需要聘用外包人员进行开发处理

3.1 当聘用的外包人员和正式员工没有超出公司规定数时,且能完全处理需求,则直接执行任务

3.2 当聘用的外包人员和正式员工超出公司规定数时,而需求池的需求没能完全处理,就会直接根据应急方法处理

3.3 当聘用的外包人员处理完需求时,且没有需求落入他们手中,就会解雇他们(这也暗示出外包人员的可悲)

线程池和相关的概念已经说完,说说Java自身提供的几种固定线程池模式(但阿里的JAVA规范手册中,不建议直接使用,建议自身创建使用)

Java提供的固定线程池模式

FixedThreadPool

提供可重复的固定线程池,超出的线程池会在队列中等待,源码

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

可以看出,核心线程数=最大线程数,使用的是LinkedBlockingQueue队列,使用默认的AboardPolicy政策

由于使用LinkedBlockingQueue队列,该队列没有最大值,故keepAliveTime是无效的,设置成0,因为根本不会创建非核心线程。

CachedThreadPool

一个没有核心线程,最大线程数=Integer.MAX的线程池

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

核心线程=0,即全部都是非核心线程,并且没有上限。

keepAliveTime是60秒,就是说空闲线程等待新任务60秒,超时则销毁。此处用到的队列是阻塞队列

SynchronousQueue,这个队列没有缓冲区,所以其中最多只能存在一个元素,有新的任务则阻塞等待。

SingleThreadExecutor 

核心线程和最大线程数=1,且采用LinkedBlockingQueue队列

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

ScheduledThreadPool

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

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue());
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值