JUC并发编程(7)---线程池画图详解

一、什么是线程池?

说线程池就要先说池化技术:在面向对象编程中,由于反复创建和销毁线程是十分消耗资源的,所以我们要尽可能减少创建和销毁的次数,池化技术是线程池的核心,就是事先创建若干个可执行的线程放入一个池中,需要的时候就从池中获取线程,使用完毕不用销毁线程而是放回池中,使得线程可以重复利用,从而减少创建和销毁线程对象的开销,并且通过线程池可以管理线程。

线程池需要熟练掌握以下几点:
1、3个方法
2、7种参数
3、4种拒绝策略

二、3个方法

Executors工具类有3大方法来创建线程池,分别是
Executors.newSingleThreadExecutor() //单个线程处理任务
Executors.newFixedThreadPool(参数) //创建一个固定大小的线程池
Executors.newCachedThreadPool() //可伸展, 任务多则线程多, 任务少线程少

注意:最后一个newCachedThreadPool的线程池,底层设置的是Integer.MAX_VALUE,也就是说可以无限大
在这里插入图片描述
分别测试一下

package com.yx.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class demo1 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程处理任务
//        ExecutorService threadPool1 = Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
//        ExecutorService threadPool2 = Executors.newCachedThreadPool();//可伸展的,任务多线程多,任务少线程少

        try {
            for (int i = 0; i < 10; i++) {
                //使用线程池创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"->Ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

根据底层源码+阿里巴巴开发手册说明,线程池不允许用Executors创建,因为可能会导致OOM(内存溢出)问题。要用ThreadPoolExecutor创建,这三个方法了解一下即可,下面是阿里巴巴开发手册说明。

在这里插入图片描述

三、7大参数

上面说到有7大参数,我们来看看哪7大,下面是源码,共有7个
在这里插入图片描述
corePoolsize,线程池的基本大小
maximumPoolSize,线程池中允许的最大线程数
keepAliveTime,等待时间(超过这个时间就释放回线程池)
TimeUnit,等待时间的单位,是秒/分/时
BlockingQueue,阻塞队列
Excutors.defaultThreadFactory,创建线程的工厂
defaultHandler,拒绝策略

我们举一个例子让大家明白这7个参数,假设一个银行有5个办理业务的窗口,但平时一般只开两个窗口办业务,如果人多,再开其他窗口

注意:人–任务,业务窗口—线程,等候区–阻塞队列
在这里插入图片描述
如图,办理业务的所有窗口就是最大线程数maximumPoolSize,这里是5个,而只开2个窗口,就表示corePoolsize基本大小为2,就是默认一开始开两个窗口。突然人越来越多,等候区也坐满了,就是阻塞队列BlockingQueue满了,并且还在进人
在这里插入图片描述
那么银行经理(线程池管理线程)看到人太多,排长队了,于是就通知开新窗口
在这里插入图片描述
这时,等候区的人就少了很多,后面可能还有在等候区等候的,但是远远少了很多。慢慢的快到中午了,人数少了很多,不需要这么多窗口了,经理见状等了一等,看着等候区也没啥人,人真的不多了,不需要这么多窗口了,于是就关闭之前开的三个。这个等待时间keepAliveTime,就是等闲下来的时候,再等keepAliveTime,如果超过这个时间,那么就关闭额外开的线程。如keepAliveTime写3,TimeUnit设为秒,就是等待3秒,3秒后释放新开的三个线程,这样就可以做到根据任务量灵活控制,先给出下面代码,其中AbortPolicy是四种拒绝策略的其中一种,关于拒绝策略后面会讲。

package com.yx.pool;

import java.util.concurrent.*;

public class demo2 {
    public static void main(String[] args) {
        ExecutorService  threadPool= new ThreadPoolExecutor(
            2,
            5,
            3,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(3),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
        );

        try {
            for (int i = 0; i < 10; i++) {
                //使用线程池创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"->Ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

在这里插入图片描述
AbortPolicy是此处的拒绝策略,也就是当线程池中所有线程都被使用,并且阻塞队列也全满了,然后AbortPolicy拒绝策略就会抛出异常,上面就是最大线程数maximumPoolSize=5,阻塞队列大小为3,所以打印了8条信息,而for (int i = 0; i < 10; i++)表示要执行10个任务,在执行8个以后,还要执行两个,但是此时线程全被占用,阻塞队列也全满了,所以遇到第九个任务就会触发AbortPolicy拒绝策略,这个策略是不处理,抛出异常,每个拒绝策略的拒绝方式都不一样。

四、4种拒绝策略

在这里插入图片描述

4.1 AbortPolicy

现在我们来讲这四个策略,四个策略和阻塞队列有关
假设到了下午,人又开始多了起来,等候区又坐了很多人,又需要开窗口了。
在这里插入图片描述

但是虽然全部的窗口都开了,人还是在进,等候区也满了,意思就是最大线程数的5个全部都在占用,并且阻塞队列也全占满了,这时就需要拒绝策略
在这里插入图片描述
我们在代码里看看

new ThreadPoolExecutor.AbortPolicy()//银行满了,还有人进来,不处理这个人,抛出异常

AbortPolicy拒绝策略就是不处理,抛出异常

当执行5个任务时,表示2个任务正在被线程处理,3个在阻塞队列等待,也就是2个在办理业务,3个在等候区等候,这时就用两个线程即可

在这里插入图片描述
代码运行后,可以看到就只有1和2两个线程在处理,corePoolSize表示线程池的基本大小,这里面基本的线程够用就不会用其他的,只有5个任务,那么只要3个在阻塞队列等,2个占用线程执行即可
在这里插入图片描述
在这里插入图片描述

当执行7个任务时,就会新调用两个线程,执行代码,这时就1,2,3,4这四个线程,最大线程数已经设为5,所以最多只能有5个任务同时被5个线程处理,阻塞队列最多只能有3个等候(代码中设置为3),所以最大承载为8,max+阻塞队列。
在这里插入图片描述
在这里插入图片描述
当有9个任务要处理时,最后一个就会抛出异常,RejectedExecutionException
在这里插入图片描述
总结:所以AbortPolicy就是线程全部被占用,阻塞队列也满了,达到最大承载时,还有任务进来时,就抛出异常

4.2 CallerRunsPolicy

这里拒绝策略就是"哪来的去哪里",就是这个属于哪个线程的就让它去找这个线程,通俗讲就是我们很忙,没时间,你找别人吧
在这里插入图片描述
在这里插入图片描述
第9个由于是main下的,只能回去找main线程了,所以又main处理了

4.3 DiscardPolicy

达到最大承载,不抛出异常,直接丢掉再进来的任务
在这里插入图片描述

4.4 DiscardOldestPolicy

相当于DiscardPolicy升级版,会判断第一个线程是否处理完任务,如果没有,再丢掉任务,也不会抛出异常,也就是说会再努力争取一下
在这里插入图片描述

五、附加思考:maxinumPoolSize如何确定?

1、CPU密集型

由于每个人的电脑能同时并行的线程不一样,这个可以看任务管理器,我这里逻辑处理器是16,就表示支持16个线程并行,也就是8核16线程,那么maxinumPoolSize就可以设置为16,但是这就有一个问题,每个人电脑配置不一样,所以我们可通过代码获取电脑可以并行的线程数,也可以直接在任务管理器看自己电脑性能配置。
在这里插入图片描述

在这里插入图片描述

2、IO密集型

IO密集型就是通过判断系统中十分耗资源的IO操作,然后定义最大线程大于这个数量即可,一般取它的两倍

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小样x

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值