Java线程池

故事
       某公司
一、
       工作内容:搬箱子。
       每天搬一个箱子(搬完就下班,任性),新来的箱子怎么办?招人!招人!某公司急需搬箱子员工若干(新来的箱子数量)名,
       朝九晚五,工作轻松,有意者联系。每天箱子数量不固定,好了公司一天很多时间不是结算工资就是招人。 
二、
       老板想了个办法,调研一下市场,看看公司每个月能来多少个箱子,每天需要处理多少箱子,每个员工一天最多处理多少个箱子。
       回来做一下整理,开始想办法,箱子随时都回来?大半夜让我起来上班怎么能行,建个仓库来了就放在哪里上班时间再弄。每个
       员工每天不能搬一个箱子就下班了,一天搬....就搬6个吧。老板拍了拍肚子上的一块腹肌开心的笑了,第二天就去招固定员工签
       合同。
三、
       公司几个员工开始上班,“滴滴”,来箱子了,员工甲冲了上去开始工作;滴滴”,又来箱子了,员工甲冲了上去开始工作.......
       核心工作人员都开始忙了,新来的箱子就放在了仓库。如果仓库也满了,老板就会去把轮休的几个员工叫回来加快处理。还忙不
       过来,老板就会想别的解决办法了。
类比
  • 公司 = 应用程序
  • 员工 = 工作线程
  • 招人 = 创建线程
  • 箱子 = 任务
  • 仓库 = 任务队列

  上述故事中公司刚开始对于工作的处理方式,呆呆的,箱子少轻轻松松,箱子多忙的一批。类似传统BIO编程中socket接入的处理方式,监听到新来的socket就创建新线程处理socket IO读写。
  当连接数小的时候可以正常运行,当连接数大的时候会创建很多的线程占用内存(可能导致内存溢出),如此多的线程要运行会导致频繁的CPU调度,即频繁的上下文切换(将当前CPU中运行的线程现场保存,按照调度方式找到下一个幸运的线程来运行)。
  如何解决上述问题呢?——使用线程池

Java线程池

  通过ThreadPoolExecutor进行分析
在这里插入图片描述

Executor接口

  作为线程池的顶级接口,只提供了一个方法。

 void execute(Runnable command)
          在未来某个时间执行给定的命令。提交任务到线程池。
ExecutorService接口

  提供了操作执行程序的方法:

  • 添加了线程池生命周期管理的方法。判断当前执行程序状态是否关闭、关闭后任务是否都完成。shutdown()是拒绝新任务,shutdownNow()拒绝新任务的提交并且试图停止正在执行的所有任务,暂停处理正在等待的任务,返回等待执行的任务列表。

  • 提交一个带返回结果(Future)的任务(Runnable、Callable)的方法submit(…)。

AbstractExecutorService抽象类

  提供ExecutorService的默认实现。此类使用 newTaskFor 返回的 RunnableFuture 实现 submit、invokeAny 和 invokeAll(批量提交) 方法,实现了如何提交任务和提交的任务类型。

ThreadPoolExecutor类

  是Java线程池框架的主要实现类。
  构造函数:


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

corePoolSize - 基本大小,池中所保存的线程数,包括空闲线程。核心线程数
maximumPoolSize - 最大大小,池中允许的最大线程数。
keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit - keepAliveTime 参数的时间单位。
workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
threadFactory - 执行程序创建新线程时使用的工厂。
handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。

  keepAliveTime指的是当任务执行结束大于核心线程数的空闲线程的存活时间,验证代码如下,等执行一会后,手动在控制台输入111回车即可知道当前池中当前的线程数:

    public static void main(String[] args) {
       ThreadPoolExecutor executor = new ThreadPoolExecutor(1,
               3,
               2L,
               TimeUnit.SECONDS,
               new ArrayBlockingQueue<>(1));
       //指定空闲线程存活时间2s
       for (int i = 0; i < 20; i++) {
           try {
               executor.execute(() -> {
                   try {
                       Thread.sleep(1000L);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println("running....");
                   System.out.println(executor.getPoolSize());
               });
           } catch (RejectedExecutionException e) {
               System.out.println(e.getMessage());
           }
       }
       Scanner scanner = new Scanner(System.in);
       String res = scanner.nextLine();
       if (res.equals("111")) {
           System.out.println(executor.getPoolSize());
       }
   }
线程池运行状态
  • RUNNING:接受新任务并处理排队任务
  • SHUTDOWN:不接受新任务,但处理排队任务
  • STOP:不接受新任务,不处理排队任务,并中断正在进行的任务
  • TIDYING:整理,所有任务都已终止,workerCount为零,线程转换到状态TIDYING将运行terminate()钩子方法
  • TERMINATED:终止,terminate()已完成

状态切换条件:

RUNNING -> SHUTDOWN:在调用shutdown()时,可能隐含在finalize()中
(RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()
SHUTDOWN -> TIDYING:当队列和池都为空时
STOP -> TIDYING:当池为空时
TIDYING -> TERMINATED: terminate()钩子方法完成后

提交任务

1、如果当前运行线程数小于corePoolSize,创建新的线程执行任务。
2、否则添加到阻塞任务队列。
3、如果任务队列满了,尝试创建新线程执行任务,如果当前工作线程数>maximumPoolSize线程池允许的最大线程数。则执行RejectedExecutionHandler。

拒绝策略
  • AbortPolicy,中止策略,直接抛出异常。
  • CallerRunsPolicy,调用者执行,由调用线程执行任务r.run()。
  • DiscardPolicy,直接删除任务。
  • DiscardOldestPolicy,废弃最旧的任务(排队最久的,即队列头元素),然后重试execute(重复此过程)。
参考资料
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值