Java线程池以及核心参数

多线程与线程池

面试小提问:
当一个线程池的核心线程是7,最大线程是20,阻塞队列是50的容量,当100的并发进来怎么分配?(答案在后面)

在任务较多的时候,使用多线程无疑是可以快速提交任务的方法,创建线程并启动一共有4种方式:/**

  • 继承Thread
  • 实现Runnable接口
  • 实现Callable接口+FutureTask(可以拿到返回结果,可以处理异常)
  • 线程池
    前三种都无法回收资源进行合理调度,所以我们在业务中都是使用线程池来多任务调度并保证资源合理分配。

使用线程池

线程池的创建:

1.使用Executors工具类创建四种默认的线程池
2.使用原生的方式创建一个:ThreadPoolExecutor executor = new ThreadPoolExecutor();
我们看一下ThreadPoolExecutor 的源码
在这里插入图片描述
在这里插入图片描述
最后使用的是ExecutorService接口,而原生创建的我们通过源码发现也是用的这个
在这里插入图片描述
我们自定义线程池时候,需要填写一些参数,最完整的有七个
在这里插入图片描述
这七个就是俗称的线程池七大参数:他们分别是 核心线程数、最大线程数、存活时间、时间单位、阻塞队列、线程工厂、以及拒绝策略。 略微有点难记我们打一个比方:
把一个线程池当作银行网点窗口来看待,假设一个银行网点有5个窗口(最大线程数),一般不忙的话我们发现只有3个窗口在营业(核心线程数),当我们去办理业务的时候如果这三个窗口都在工作,我们就回去等待席等待(阻塞队列),可是如果当等待席满人后还有很多顾客来办理业务呢?这时候就会叫剩余两个空闲窗口的同事来加班帮忙处理(线程工厂)。当人没那么多的时候,加班的同事就要回去继续休息了(不那么忙的时间为存活时间,时间单位就也可以理解啦),但是如果加班特别忙外加等待席也满了大堂经理就会来想一些办法处理这个问题,比如张贴说本营业厅业务繁忙请去别的营业厅(拒绝策略),怎么样这就很好的记住了线程池的核心参数和作用。
corePoolSize:是线程池创建后就一直存在的线程,除非添加了allowCoreThreadTimeOut这个参数,才会在空闲等待时候回收线程池(有什么用呢?面试时候被问到线程池一直都有线程吗的时候可以快速反击!!!)
maximumPoolSize:控制最大资源数并发的
keepAliveTime:当前线程数量大于核心线程时,只要大于这个存活时间多于的线程会回收
BlockingQueue workQueue:阻塞队列,也是自定义一个线程池比较关键的原因,因为阻塞队列中LinkedBlockQueue的默认最大值为Integer的最大值,基本感受不到边界可能会内存占满。当一个任务过来后,如果线程不够则会来阻塞队列进行排队,只要有线程空闲就会来解决任务
ThreadFactory:线程工厂,一般默认。当自定义一些线程的名字时候可以自定义规则。
RejectedExecutionHandler:拒绝策略,当线程池所接收的任务超出所承受的上限(最大线程数+阻塞队列容量)时会触发,而拒绝策略有四种:

  • AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行

  • CallerRunsPolicy:”调用者运行“一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量

  • DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务

  • DiscardPolicy: 直接丢弃任务,不予任何处理也不抛异常。如果允许任务丢失,这是最好的一种方案

线程池工作流程

  1. 在创建了线程池之后,等待提交过来的 人物请求。

  2. 当调用execute()方法添加一个请求任务时,线程池会做出如下判断

    2.1 如果正在运行的线程数量小于corePoolSize,那么马上船舰线程运行这个任务;

    2.2 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;

    2.3如果此时队列满了且运行的线程数小于maximumPoolSize,那么还是要创建非核心线程立刻运行此任务

    2.4如果队列满了且正在运行的线程数量大于或等于maxmumPoolSize,那么启动饱和拒绝策略来执行

  3. 当一个线程完成任务时,他会从队列中却下一个任务来执行

  4. 当一个线程无事可做超过一定的时间(keepAliveTime)时,线程池会判断:

    如果当前运行的线程数大于corePoolSize,那么这个线程会被停掉;所以线程池的所有任务完成后他最大会收缩到corePoolSize的大小

Executor默认的线程池也有很多,最常见的有四种:
  • Executors.newFixedThreadPool(int)

执行长期的任务,性能好很多

创建一个定长线程池,可控制线程最大并发数,炒出的线程回在队列中等待。

newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是想到等的,他使用的是LinkedBlockingQueue

  • Executors.newSingleThreadExecutor()

    一个任务一个任务执行的场景

    创建一个单线程的线程池,他只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行

    newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,使用LinkedBlockingQueue

  • Executors.newCachedThreadPool()

    执行很多短期异步的小程序或负载较轻的服务器

    创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲县城,若无可回收,则新建线程。
    newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60s,就销毁线程

  • Executors.newScheduledThreadPool()

使用线程池的好处:

  • 降低资源的消耗
  • 通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
  • 提高响应速度
  • 因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
  • 提高线程的可管理性
    -线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来
    的系统开销。无限的创建和销毁
    定时周期行任务执行场景
    创建一个定长的线程池,优点是支持定期使用定时任务执行。
    小提问回答:
    • 7个任务会得到立刻执行
    • 50个会进入到队列
    • 再开13个进行执行(总计处理了70个请求)
    • 剩下的30个会使用拒绝策略来执行,一般是丢弃。
    • 如果不想也可以使用CallerRunPolicy策略以同步的方式直接使用Runable的run方法来执行
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值