Java线程池

1.线程池

1.1线程池是什么

线程池是一种利用池化技术思想来实现的线程管理技术,主要是为了复用线程、便利地管理线程和任务、并将线程的创建和任务的执行解耦开来。我们可以创建线程池来复用已经创建的线程来降低频繁创建和销毁线程所带来的资源消耗。**线程池可以设置一定数量的空闲线程,这些线程即使没有任务时仍然不会释放.线程池也可以设置最大线程数,防止任务过多压垮服务器.**在JAVA中主要是使用ThreadPoolExecutor类来创建线程池,并且JDK中也提供了Executors工厂类来创建线程池

线程池还有以下优点:

  1. 降低资源消耗,复用已创建线程可以降低创建\销毁线程的消耗.
  2. 提高响应速度.使用线程池,在任务需要处理是可以不等待线程的创建立即执行.
  3. 提高线程的可管理性.使用线程池能统一的分配,调优和监控.

总而言之,线程池最大的好处就是减少每次启动\销毁线程的损耗.

1.2为什么需要线程池

若是没有线程池,创建线程执行任务就如下图所示:

在这里插入图片描述

可以看到,每执行一个任务都要创建新的线程,一直这样创建线程会产生很大的开销,且无法统一的管理每个创建的线程.

1.3标准库中的线程池

1.3.1 Executors

使用Executors.newFixedThreadPool(10)能创建出固定包含10个线程的线程池,其返回值类型为ExecutorService,通过ExecutorService.submit可以注册一个任务到线程池中.

ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
});

Executors创建线程池的几种方式:

  • newFixedThreadPool:创建固定线程数的线程池
  • newCachedThreadPool:创建线程数目动态增长的线程池.
  • newSingleThreadExecutor:创建只包含单个线程的线程池.
  • newScheduledThreadPool:设定延迟时间后执行命令,或者定期执行命令,是进阶版的Timer.

Executors本质上是ThreadPoolExecutor类的封装

1.3.2 ThreadPoolExecutor

ThreadPoolExecutor是应用最广的低层线程池类,它实现了ExecutorExecutorService接口.以下是ThreadPoolExecutor的构造方法和几个最常用的方法:

public class ThreadPoolExecutor extends AbstractExecutorService {
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory treadFactory,RejectedExecutionHandler  handler) {}
    @Override
    public void execute(Runnable command) {}
    @Override
    public void shutdown() {}
    @Override
    public List<Runnable> shutdownNow() {}
    @Override
    public boolean isShutdown() {}
    @Override
    public boolean isTerminated() {}
    @Override
    public boolean awaitTermination(long timeout,TimeUnit unit) throw InterruptedException {}
    ...
}

[解释]:

  • corePoolSize:核心线程数
  • maximumPoolSize:最大线程数
  • keepAliveTime:如果池中当前有超过 corePoolSize 个线程,则多余的线程如果空闲时间超过 keepAliveTime(请参阅 getKeepAliveTime(TimeUnit) )将被终止
  • TimeUnit:允许空闲的时间.keepaliveTime的时间单位是时分秒以及其他值
  • workQueue:传递任务的阻塞队列
  • threadFactory:执行程序创建新线程时使用的工厂
  • handler:由于达到线程边界和队列容量而阻塞执行时使用的处理程序.有四种预定义的处理程序策略:
    • AbortPolicy():处理程序在决绝是抛出运行时RejectedExecutionException,此时所有任务全部停止
    • CallerRunsPolicy()😗*调用execute的线程本身运行任务.**这提供了一种简单的反馈机制,可以减慢提交新任务的速度. 可以简单理解为谁给的任务谁解决.
    • DiscardOldestPolicy():如果执行器未关闭,工作队列头部的任务将被丢弃,然后重试执行(可能会再次失败导致重复).这种策略很少被接受.在几乎所有情况下,还应该取消在任何等待其完成的组件中导致异常的任务,并且/或者记录失败信息,如 ThreadPoolExecutor.DiscardOldestPolicy 文档中所示.因为在队列中,头部的任务是队列中最老的任务,所以换句话说处理方式就是丢弃队列中最老的任务.
    • DiscardPolicy():无法执行的任务会被简单地丢弃。此策略仅适用于从不依赖任务完成的极少数情况。此处可以简单理解为把新来的任务丢弃.

关于keep Alive Time的设置

以上内容是Java API文档原文解释与部分个人理解,下面我举个例子以便理解:

corePoolSize就像公司里的正式员工,MaximumPoolSize则是正式员工+实习生.当工作任务多的时候就会招聘实习生来缓解压力,任务少的时候则把实习生裁掉减小开销.但是呢什么时候任务多什么时候任务少是不确定的,因此需要留个观察期KeepAliveTime.如果在keepAliveTime内没有什么繁琐的任务那大概后面也暂时不会有,所以超过keepAliveTime就把实习生裁掉,而TimeUnit就是用来设置允许实习生“摸鱼”(keepAliveTime)的时间.

希望我这样说能对各位有帮助.

1.3.3 线程池的工作流程

关于最大线程数MaximumPoolSize的达到条件:假设核心线程数corePoolSize为3,最大线程数MaximumPoolSize为8,keepAliveTime为3秒.随着任务数量上升,线程池会不断创建线程直到达到核心线程数corePoolSize3,就不创建线程了.此时多余的任务通过加入阻塞队列BlockingQueue来运行,当超过阻塞队列+核心线程数时,线程池不得不扩大线程个数来满足当前任务的运行,这是就需要创建新的线程,直到数量达到8(最大线程数).

ThreadPoolExecutor里的方法还有很多,这里只是介绍了以下构造方法,剩下的内容可以查看[Java API文档](Java17中文文档 - API参考文档 - 全栈行动派 (qzxdp.cn)).个人建议是英文文档和翻译成中文的 文档对照着看,当中文文档看着很怪时可以去看看原文是怎么说的.

  • 39
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值