线程池核心参数讲解

为什么用线程池?

1.降低资源消耗;提高线程利用率,降低创建和销毁线程的消耗。

2.提高响应速度,任务来了,直接有线程可用执行,而不是先创建线程,再执行。

3.提高线程的可管理性,线程是稀缺资源,使用线程池可以统一分配调优监控。

线程池7大参数?

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

这几个参数的含义分别是:

参数名参数含义
corePoolSize核心线程数
maximumPoolSize最大线程数
keepAliveTime空闲线程存活时间
unit时间单位
workQueue线程池任务队列
threadFactory创建线程的工厂
handler拒绝策略

1.corePoolSize

核心线程数,是指线程池中长期存活的线程数。int 类型。可以理解为长期存在的、“常驻的”线程。就好比是地主家的长工一样,无论这一年的或多还是活少,都不会被辞退。

2.maximumPoolSize

最大线程数,是指线程池允许创建的最大线程数量。最大线程数的数量包含核心线程数。int 类型。比如,地主家的活太多了,光要长工可能干不完,需要一些临时工来帮忙,长工的数量 + 临时工的数量 = 最大线程数。

需要注意的是,在创建线程池的时候,最大线程数 maximumPoolSize 不能小于核心线程数 corePoolSize,否则会在程序运行时报异常:
核心线程数为 2,最大线程数为 1,报 java.lang.IllegalArgumentException 异常,但是最大线程数可以等于核心线程数。

3.keepAliveTime

空闲线程存活时间,指当线程池中没有任务是,会销毁一些线程,销毁的线程数 = maximumPoolSize - corePoolSize。long 类型。

4.unit

时间单位,指空闲线程存活时间单位,与 keepAliveTime 配合使用。单位为 TimeUnit,常用取值有以下 7 个:

参数含义
TimeUnit.DAYS
TimeUnit.HOURS小时
TimeUnit.MINUTES分钟
TimeUnit.SECONDS
TimeUnit.MILLSECONDS毫秒
TimeUnit.MICROSECONDS微秒
TimeUnit.NANOSECONDS纳秒

5.workQueue

阻塞队列,指线程池存放任务的队列,用来存储线程池的所有待执行的任务。BlockingQueue 类型。取值有以下几种:

参数含义
ArrayBlockingQueue一个由数组结构组成的有界阻塞队列
LinkedBlockingQueue一个由链表结构组成的有界阻塞队列
SynchronousQueue一个不存储元素的阻塞队列,即直接提交给线程,不保持它们
PriorityBlockingQueue一个支持优先级排序的无界阻塞队列
DelayQueue一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素
LinkedTransferQueue一个由链表结构组成的无界阻塞队列。与 SynchronousQueue 类似,还含有非阻塞方法
LinkedBlockingDeque一个由链表结构组成的双向阻塞队列

比较常用的是 LinkedBlockingQueue。线程池的排队策略和该参数息息相关。

6.threadFactory

线程工厂。线程池创建线城市调用的工厂方法,通过此方法可以设置线程的优先级、线程的命名规则以及线程的类型(用户线程还是守护线程)等。ThreadFactory 类型。

public static void main(String[] args) {
    // 创建线程工厂
    ThreadFactory threadFactory = new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            // 创建线程池中的线程
            Thread thread = new Thread(r);
            // 设置线程名称
            thread.setName("线程-" + r.hashCode());
            // 设置线程优先级
            thread.setPriority(Thread.MAX_PRIORITY);
            // 设置线程类型(守护线程、用户线程), false-用户线程
            thread.setDaemon(false);
            return thread;
        }
    };
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2,
            0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), threadFactory);
    threadPoolExecutor.submit(new Runnable() {
        @Override
        public void run() {
            Thread thread = Thread.currentThread();
            System.out.println(String.format("线程: %s, 线程优先级: %d",
                    thread.getName(), thread.getPriority()));
        }
    });
}

7.handler

拒绝策略。当线程池的任务超出线程池队列可以存储的最大值之后,拒绝任务的策略。RejectedExecutionHandler 类型。取值有:

参数含义
AbortPolicy拒绝并抛出异常
CallerRunsPolicy使用当前调用的线程来执行此任务
DiscardOldestPolicy抛弃队列头部(最旧)的一个任务,并执行当前任务
DiscardPolicy忽略并抛弃当前任务

线程池的默认拒绝策略是 AbortPolicy–拒绝并抛出异常。

8.案例

    @Test
    public void test7(){
        ExecutorService executorService=
                new ThreadPoolExecutor(3,5,1L, TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),
                        Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

        for (int i = 0; i < 10; i++) {
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName()+"===>办理业务");
            });
        }
        executorService.shutdown();
    }

9.线程池的底层工作原理

线程池内部是通过队列+线程实现的,当我们利用线程池执行任务时:

  1. 如果此时线程池中的线程数量⼩于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建
    新的线程来处理被添加的任务。

  2. 如果此时线程池中的线程数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放⼊
    缓冲队列。

  3. 如果此时线程池中的线程数量⼤于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数
    量⼩于maximumPoolSize,建新的线程来处理被添加的任务。

  4. 如果此时线程池中的线程数量⼤于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等
    于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。

  5. 当线程池中的线程数量⼤于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被
    终⽌。这样,线程池可以动态的调整池中的线程数

10.线程池中阻塞队列的作用?什么是先添加队列而不是先创建最大线程?

1、⼀般的队列只能保证作为⼀个有限⻓度的缓冲区,如果超出了缓冲⻓度,就⽆法保留当前的任务了,
阻塞队列通过阻塞可以保留住当前想要继续⼊队的任务。

阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进⼊wait状态,释放cpu资
源。

阻塞队列⾃带阻塞和唤醒的功能,不需要额外处理,⽆任务执⾏时,线程池利⽤阻塞队列的take⽅法挂
起,从⽽维持核⼼线程的存活、不⾄于⼀直占⽤cpu资源。

2、在创建新线程的时候,是要获取全局锁的,这个时候其它的就得阻塞,影响了整体效率。

就好⽐⼀个企业⾥⾯有10个(core)正式⼯的名额,最多招10个正式⼯,要是任务超过正式⼯⼈数
(task > core)的情况下,⼯⼚领导(线程池)不是⾸先扩招⼯⼈,还是这10⼈,但是任务可以稍微积
压⼀下,即先放到队列去(代价低)。10个正式⼯慢慢⼲,迟早会⼲完的,要是任务还在继续增加,超
过正式⼯的加班忍耐极限了(队列满了),就的招外包帮忙了(注意是临时⼯)要是正式⼯加上外包还
是不能完成任务,那新来的任务就会被领导拒绝了(线程池的拒绝策略)。

11.线程池中线程复⽤原理

线程池将线程和任务进⾏解耦,线程是线程,任务是任务,摆脱了之前通过 Thread 创建线程时的⼀个
线程必须对应⼀个任务的限制。

在线程池中,同⼀个线程可以从阻塞队列中不断获取新任务来执⾏,其核⼼原理在于线程池对 Thread
进⾏了封装,并不是每次执⾏任务都会调⽤ Thread.start() 来创建新线程,⽽是让每个线程去执⾏⼀
个“循环任务”,在这个“循环任务”中不停检查是否有任务需要被执⾏,如果有则直接执⾏,也就是调⽤
任务中的 run ⽅法,将 run ⽅法当成⼀个普通的⽅法执⾏,通过这种⽅式只使⽤固定的线程就将所有任
务的 run ⽅法串联起来。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值