池化技术应用:线程池、数据库连接池、http连接池等等。

池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。

使用线程池的好处:

  • 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度:当任务到达时,可以不需要等待线程创建就能立即执行。
  • 管理线程:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,监控和调优。

Executor框架

绿色实线箭头是继承,虚线是接口实现

Executor接口是Executor框架的一个最基本的接口,Executor框架的大部分类都直接或间接地实现了此接口。

Executor接口只有一个execute(Runnable command)方法。

public void execute(Runnable r) {new Thread(r).start();
}

ExecutorService接口继承了Executor接口,该接口用于管理线程。

public interface ExecutorService extends Executor {
// 请求关闭、发生超时或者当前线程中断,无论哪一个首先发生之后,都将导致阻塞,直到所有任务完成执行。
boolean awaitTermination(long timeout, TimeUnit unit);// 执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks);// 执行给定的任务,当所有任务完成或超时期满时,返回保持任务状态和结果的 Future 列表。
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit);// 任务列表里只要有一个任务完成了,就立即返回。而且一旦正常或异常返回后,则取消尚未完成的任务。
<T> T invokeAny(Collection<? extends Callable<T>> tasks);// 执行给定的任务,如果在给定的超时期满前某个任务已成功完成(也就是未抛出异常),则返回其结果。
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit);// 如果此执行程序已关闭,则返回 true。
boolean isShutdown();// 如果关闭后所有任务都已完成,则返回 true。先调用 shutdown 或 shutdownNow,否则 isTerminated 永不为 true。
boolean isTerminated();// 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
void shutdown();// 通过调用interrupt试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
List<Runnable> shutdownNow();// 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future,该Future的get方法在成功完成时将会返回给定的结果。
<T> Future<T> submit(Callable<T> task);Future<?> submit(Runnable task);<T> Future<T> submit(Runnable task, T result);
}

execute和submit方法

  • execute,执行一个任务,没有返回值。
  • submit,提交一个线程任务,有返回值。
  • submit(Callable<T> task)能获取到它的返回值,通过future.get()获取(阻塞直到任务执行完)。
  • submit(Runnable task, T result)能通过传入的载体result间接获得线程的返回值。

接下来介绍线程池主要实现类ThreadPoolExecutor

ThreadPoolExecutor构造函数

//五个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue)
//第六个参数
ThreadFactory threadFactory//第七个参数
RejectedExecutionHandler handler

ThreadPoolExecutor类的构造函数有4个,前面5个参数是必须的,第6和7个参数同这5个参数又构成了3个构造函数。

int corePoolSize

  • 线程池中核心线程数最大值,线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corePoolSize,进入任务队列。
  • 核心线程默认情况下会一直存活在线程池中,即使这个核心线程是闲置状态。如果指定ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true,那么闲置状态的核心线程,超过一定时间(keepAliveTime),就会被销毁掉。

int maximumPoolSize

  • 该线程池中线程总数最大值。如果等待队列满了,创建非核心线程。
  • 线程总数 = 核心线程数 + 非核心线程数。

long keepAliveTime

  • 该线程池中非核心线程闲置超时时长。
  • 一个非核心线程,如果不干活(闲置状态)的时长超过这个参数所设定的时长,就会被销毁掉。
  • 如果设置allowCoreThreadTimeOut = true,则会作用于核心线程。

TimeUnit unit

  • keepAliveTime的时间单位。

BlockingQueue<Runnable> workQueue

  • 该线程池中的任务队列,维护着等待执行的Runnable对象。
  • 当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务。

队列的三种通用策略详解:

直接提交 SynchronousQueue

将任务直接提交给线程而不保存它们。如果所有线程都在工作,就新建一个线程来处理这个任务,所以通常要求maximumPoolSizes设置为Integer.MAX_VALUE,即无限大,以避免拒绝新提交的任务。

无界队列 LinkedBlockingQueue

将导致在所有核心线程都在忙时新任务在队列中等待,创建的线程就不会超过 corePoolSize,maximumPoolSize 的值也就没意义了。

有界队列 ArrayBlockingQueue

可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误。

延时队列 DelayQueue

传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务。

ThreadFactory threadFactory

用于创建新线程,是一个接口,new他的时候需要实现他的Thread newThread(Runnable r)方法,一般不用。

RejectedExecutionHandler handler

用于抛出异常,一般不用。

  • ThreadPoolExecutor.AbortPolicy()  抛出java.util.concurrent.RejectedExecutionException异常,默认。
  • ThreadPoolExecutor.CallerRunsPolicy()  重试添加当前的任务,他会自动重复调用execute()方法。
  • ThreadPoolExecutor.DiscardOldestPolicy()  抛弃旧的任务
  • ThreadPoolExecutor.DiscardPolicy()  抛弃当前的任务。

如何配置线程池

CPU密集型任务

CPU核心数+1,CPU 密集型任务使得CPU使用率很高,内存、硬盘、网络占用的时间少于cpu本身计算的时间,这时应配置尽可能小的线程避免线程之间频繁的切换消耗资源。

IO密集型任务

2*CPU核心数,O密集型任务CPU使用率并不高,当线程发出请求后,由于不占用cpu资源,可以阻塞等待,因此可以让CPU在等待IO的时候有其他线程去处理别的任务,充分利用CPU时间。

混合型任务
可以将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池去处理。

线程池执行策略

  • 线程数量未达到corePoolSize,则新建一个核心线程执行任务
  • 线程数量达到了corePools,则将任务移入队列等待
  • 队列已满,新建线程(非核心线程)执行任务
  • 队列已满,总线程数又达到了maximumPoolSize,就会由RejectedExecutionHandler抛出异常

常用的四种线程池

CachedThreadPool 缓存线程池

    public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); cachedThreadPool.execute(new Runnable(){public void run() {};})

特点:

  • 核心线程数为0,线程数无限制
  • 有空闲线程则复用空闲线程,若无空闲线程则新建线程
  • 空闲线程只会等60s
  • 直接提交队列

FixedThreadPool 定长线程池

    public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int theads);fixedThreadPool.execute(new Runnable(){public void run() {};});

特点:

  • 创建时给定核心线程数,所有线程都是核心线程。
  • 线程空闲就回收,核心线程默认不回收。
  • 阻塞队列无界

SingleThreadExecutor 单线程化的线程池

    public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();singleThreadExecutor.execute(new Runnable(){public void run() {};});
    static class FinalizableDelegatedExecutorServiceextends DelegatedExecutorService {FinalizableDelegatedExecutorService(ExecutorService executor) {super(executor);}protected void finalize() {super.shutdown();}}

加了个finalize方法保证线程池的关闭,DelegatedExecutorService是继承AbstractExecutorService的一个类。

和newFixedThreadPool(1)的区别

封装成FinalizableDelegatedExecutorService类,这个类就是对ExecutorService进行了一个包装,防止暴露出不该被暴露的方法,然后加上了finalize方法保证线程池的关闭。

特点:

  • 线程池只有一个核心线程
  • 线程空闲就回收,核心线程默认不回收。
  • 阻塞队列无界

ScheduledThreadPool 定长周期线程池:

ScheduledThreadPool是一个能实现定时、周期性任务的线程池。

 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {return new ScheduledThreadPoolExecutor(corePoolSize);}ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(int theads);scheduledThreadPool.schedule(new Runnable() {public void run() {System.out.println("延迟1秒执行");}}, 1, TimeUnit.SECONDS);scheduledThreadPool.scheduleAtFixedRate(new Runnable() {public void run() {System.out.println("延迟1秒后每3秒执行一次");}}, 1, 3, TimeUnit.SECONDS);
public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());}

ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,实现了ScheduledExecutorService接口,该接口定义了schedule等任务调度的方法。ScheduledThreadPoolExecutor有两个重要的内部类:DelayedWorkQueue和ScheduledFutureTask。DelayeddWorkQueue是一个阻塞队列,而ScheduledFutureTask继承自FutureTask,并且实现了Delayed接口。

特点:

  • 给定核心线程数,定时、周期性处理任务
  • 线程空闲就回收
  • 阻塞队列无界

线程池为什么能维持线程不释放,随时运行各种任务?

总结就是:如果队列中没有任务时,核心线程会一直阻塞在获取任务的方法,直到返回任务。

//重点:poll会一直阻塞直到超过keepAliveTime或者获取到任务
//take 会一直阻塞直到获取到任务
//在没有任务的时候 如果没有特别设置allowCoreThreadTimeOut,我们的核心线程会一直阻塞在这里Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();

所以也解释了workQueues为什么要是BlockingQueue

ArrayBlockingQueue的take方法

 public E take() throws InterruptedException {final ReentrantLock lock = this.lock;  //加锁lock.lockInterruptibly();try {while (count == 0)notEmpty.await();  //队列为空时,将使这个线程进入阻塞状态,直到被其他线程唤醒时取出元素return dequeue();  //消费对头中的元素} finally {lock.unlock();}}

可以看到当队列内没有任务时,调用await方法挂起线程。await方法是ConditionObject的方法,内部调用了LockSupport类的park方法将线程挂起。可以看这里。

多线程—线程池Executor框架及四种常用线程池相关推荐

  1. 四种常用线程池及自定义线程池参数详细分析

    文章目录 一.什么是线程池 二.常用的更方便的Executors工厂方法 三.自定义线程池 四.缓冲队列BlockingQueue 五. 排队的三种一般策略 六.拒绝策略 一.什么是线程池 线程池(英 ...

  2. JDK四种常见线程池及使用场景、两种提交任务的方法

    转载:JDK 提供的线程池及使用场景 - 编程猎人 (programminghunter.com) 目录 四种常见线程池 1.newFixedThreadPool 2.newSingleThreadE ...

  3. 四种Java线程池用法解析

    四种Java线程池用法解析 本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 http://www.jb51.net/article/81843.htm 1.new Thread的弊端 ...

  4. Java 线程池原理及四种常用的线程池使用

    推荐阅读:Java线程池实现原理及其在美团业务中的实践 文章目录 什么是线程池 使用线程池的好处 线程池的实现原理 流程图分析 源码分析 线程池的使用 向线程池中提交任务 newCachedThrea ...

  5. Executors类创建四种常见线程池

    文章目录 线程池架构 newSingleThreadExecutor newFixedThreadPool newCachedThreadPool newScheduledThreadPool Exe ...

  6. unixbench分析_UnixBench,Bench,SuperBench 和 Zbench 四种常用云服务器综合性能测评脚本工具的对比分析以及使用教程...

    君哥今天写了一篇不用脚本进行云服务器配置真伪鉴定,网络性能测评的文章,有童鞋询问有没有更为简单高效的测试方法呢?当然有.这篇文章以4核8G5M的阿里云服务器为例,分别采用 UnixBench. Ben ...

  7. 后台CS代码中创建四种常用的SL动画效果

    http://www.cnblogs.com/chengxingliang/archive/2011/03/07/1974436.html后台CS代码中创建四种常用的动画效果[附带源码实例] 转载于: ...

  8. [转载] java实现四种常用排序算法

    参考链接: 用Java排序 四种常用排序算法 ##注:从小到大排 ##冒泡排序## 特点:效率低,实现简单 思想:每一趟将待排序序列中最大元素移到最后,剩下的为新的待排序序列,重复上述步骤直到排完所有 ...

  9. htt【RiPro网盘链接检测插件】目前支持四种常用网盘

    介绍: 目前支持百度网盘.蓝奏云.天翼云盘.坚果云盘,后续将支持更多网盘,如果你也想自己常用的网盘加入评论或联系站长告知,谢谢! 另外如使用过程中发现有链接状态已检测结果不匹配,请带上链接找站长修复! ...

最新文章

  1. Python使用tkinter构建一个多元回归预测模型GUI界面(接受用户输入数据并给出模型推理结果)
  2. ECharts学习总结(五):echarts的Option概览
  3. 10个不为人知 但绝对值得收藏的网站
  4. webview java调用js_android WebView java与js相互调用
  5. 开源图数据库项目 DGraph 的前世今生 | 技术头条
  6. Ruby数组(2):数组方法详细整理
  7. 001如何将灰度视频处理为伪彩色
  8. Android系统中的常用时间戳及时间单位
  9. python sklearn包中的主成分分析_九、Sklearn主成分分析
  10. php 图片外链限制,将新浪微博图床图片保存到本地解决限制外链的方法
  11. Manjaro安装配置指南
  12. Nginx设置图片防盗链(白名单与黑名单)
  13. 【大数据实战项目八】使用机器学习算法进行预测分析并进行网上部署
  14. win 7 音频问题
  15. DoIP(五)—— UDSonIP
  16. 中小型研发团队架构实践三要点
  17. [项目管理]-- 项目开发流程(基本流程)
  18. JQuery基础教程:入门
  19. git push报错: nt: Updates were rejected because a pushed branch tip is behind its remote
  20. 【记录爬虫实战过程】入门学习·详细过程·爬取天气信息并通过pyecharts进行可视化展示1

热门文章

  1. centos7源代码安装mysql5.7_CentOS7源码编译安装MySQL5.7.11
  2. webpack 合并压缩_webpack的运用
  3. c语言程序设计教程岳莉答案,C++程序设计教程
  4. html调用deeplink,如何优雅地从浏览器打开本地应用deeplink
  5. android震动提示音,android的消息提示(震动与提示音)
  6. 简述旋转编码器的工作原理_什么是编码器,编码器工作原理介绍
  7. 华为手机怎么隐藏按键图标_华为手机隐藏技巧,一键简单设置,让沟通更加便捷...
  8. 用积木做了个无人机。
  9. 《FPGA入门教程》看书随笔——RTL设计
  10. python打印小猪佩琪_极度舒适的 Python 入门教程,小猪佩奇也能学会~