为什么要用线程池?
池化技术相比大家已经屡见不鲜了,线程池、数据库连接池、Http 连接池等等都是对这个思想的应用。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。
线程池提供了一种限制和管理资源(包括执行一个任务)。 每个线程池还维护一些基本统计信息,例如已完成任务的数量。
这里借用《Java 并发编程的艺术》提到的来说一下使用线程池的好处:
降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
降低销毁资源,减少创建和销毁的次数
提高相应速率
防止服务器过载,可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,导致系统崩溃
提高线程可管理性
线程池常用的方法解决
一、冲突问题
冲突导致线程不安全,所以我们要用线程安全机制
1、加synchronized
synchronized (锁对象){
可能出现线程安全问题的代码
}
或者同步方法
修饰符 synchronized 返回值类型 方法名(参数列表){
可能出现线程安全问题的代码
}
2、使用Lock锁
比如哈
Lock a = new ReentrantLock();

a.lock();
二、线程池过载
如果线程池满了怎么办
会执行线程拒绝策略
ThreadPoolExecutor提供了四个公开的内部静态类:
AbortPolicy:默认,丢弃任务并抛出RejectedExecutionException异常。
DiscardPolicy:丢弃任务,但是不抛出异常(不推荐)。
DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中。
CallerRunsPolicy:调用任务的run()方法绕过线程池直接执行。
友好的拒绝策略:
保存到数据库进行削峰填谷。在空闲时再提出来执行。
转向某个提示页面
打印日志
如何自定义拒绝策略:

线程池中共任务数量过多或者任务类型比较复杂,线程池可能会过载,导致查询性能下降或者系统不稳定。
使用拒绝策略,当线程池的数量达到最大值,任务队列满了就会拒绝
可以自定义拒绝策略,实现RejectedExecutionHandler接口
也可以用框架或者Java提供的 AbortPolicy CallerRunsPolicy DiscardPolicy DiscardOldestPolicy我们大说一下ThreadPoolExecutor里面的这些方法1、AbortPolicy:总是抛出RejectedExecutionException异常,表示拒绝执行新的任务。
/*** A handler for rejected tasks that throws a* {@code RejectedExecutionException}.*/public static class AbortPolicy implements RejectedExecutionHandler {/*** Creates an {@code AbortPolicy}.*/public AbortPolicy() { }/*** Always throws RejectedExecutionException.** @param r the runnable task requested to be executed* @param e the executor attempting to execute this task* @throws RejectedExecutionException always*/public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {throw new RejectedExecutionException("Task " + r.toString() +" rejected from " +e.toString());}}*/

2、CallerRunsPolicy:执行调用者线程中的任务,除非执行器已关闭,在这种情况下,将放弃任务。
isShutdown是什么意思
第二个方法叫作 isShutdown(),它可以返回 true 或者 false 来判断线程池是否已经开始了关闭工作,也就是是否执行了 shutdown 或者 shutdownNow 方法。
这里需要注意,如果调用 isShutdown() 方法的返回的结果为 true 并不代表线程池此时已经彻底关闭了,这仅仅代表线程池开始了关闭的流程,也就是说,此时可能线程池中依然有线程在执行任务,队列里也可能有等待被执行的任务
/* Predefined RejectedExecutionHandlers /
/
*
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/
*
* Creates a {@code CallerRunsPolicy}.
/
public CallerRunsPolicy() { }
/
*
* Executes task r in the caller’s thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
}
3、DiscardPolicy:什么都不做,这会导致丢弃任务r。
/
*
* A handler for rejected tasks that silently discards the
* rejected task.
/
public static class DiscardPolicy implements RejectedExecutionHandler {
/
*
* Creates a {@code DiscardPolicy}.
/
public DiscardPolicy() { }
/
*
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
4、DiscardOldestPolicy:获取并忽略执行器将执行的下一个任务(如果一个任务立即可用),然后重试任务r的执行,除非执行器关闭,在这种情况下,任务r将被丢弃。
/
*
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/
*
* Creates a {@code DiscardOldestPolicy} for the given executor.
/
public DiscardOldestPolicy() { }
/
*
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute®;
}
}
}
三、线程池阻塞
线程池阻塞是指当线程池中的线程数量达到最大值(maximumPoolSize)时,然后新的任务会被阻塞,导致程序性能下降。下面是一些解决线程池阻塞的方法:
1. 增加任务队列的容量:可以增加任务队列的容量,以容纳更多的任务。可以使用LinkedBlockingQueue等无界队列,也可以使用ArrayBlockingQueue等有界队列。但是,增加任务队列的容量也会增加内存开销,需要根据实际需求来进行选择。(时间和空间的平衡问题)
2. 使用预启动所有核心线程:可以在创建线程池时,使用prestartAllCoreThreads()方法预先启动所有核心线程或核心业务线程,以提高线程池的响应速度。这样,当有新任务到来时,线程池就可以立即执行任务,而不需要等待线程创建的时间。(预先处理机制)
3. 调整任务提交方式:可以考虑调整任务提交方式,以减少线程池的阻塞。例如,可以将一些独立的任务使用submit()方法提交,这样可以立即返回一个Future对象,而不需要等待任务执行完成。对于有依赖关系的任务,可以使用CompletionService来管理任务的执行。
四:线程池的工作流程
1)当提交一个新的任务到线程池时,线程池判断corePoolSize线程池是否都在执行任务,如果有空闲的线程,则从核心线程池中取一个线程来执行此任务,直到当前线程数等于corePoolSize;
2)如果当前线程数量为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;
3)如果阻塞队列满了,那就创建新的线程执行当前任务,直到线程池中的线程数达到maxPoolSize,这时候再有任务来,就由饱和策略来处理提交的任务
参数怎么设置的
corePoolSize:核心线程数。如果等于0,则任务执行完后,没有任务请求进入时销毁线程池中的线程。如果大于0,即使本地任务执行完毕,核心线程也不会被销毁。设置过大会浪费系统资源,设置过小导致线程频繁创建。
maximumPoolSize:最大线程数。必须大于等于1,且大于等于corePoolSize。如果与corePoolSize相等,则线程池大小固定。如果大于corePoolSize,则最多创建maximumPoolSize个线程执行任务
keepAliveTime:线程空闲时间。线程池中线程空闲时间达到keepAliveTime值时,线程会被销毁,只到剩下corePoolSize个线程为止。默认情况下,线程池的最大线程数大于corePoolSize时,keepAliveTime才会起作用。如果allowCoreThreadTimeOut被设置为true,即使线程池的最大线程数等于corePoolSize,keepAliveTime也会起作用(回收超时的核心线程)。
unit:TimeUnit表示时间单位。
workQueue:缓存队列。当请求线程数大于corePoolSize时,线程进入BlockingQueue阻塞队列。
threadFactory:线程工厂。用来生产一组相同任务的线程。主要用于设置生成的线程名词前缀、是否为守护线程以及优先级等。设置有意义的名称前缀有利于在进行虚拟机分析时,知道线程是由哪个线程工厂创建的。
handler:执行拒绝策略对象。当达到任务缓存上限时(即超过workQueue参数能存储的任务数),执行拒接策略,可以看作简单的限流保护。

线程池相关类结构

ExecutorService接口继承了Executor接口,定义了管理线程任务的方法。
ExecutorService的抽象类AbstractExecutorService提供了submit、invokeAll()等部分方法实现,但是核心方法Executor.execute()并没有实现。
因为所有任务都在这个方法里执行,不同的线程池实现策略会有不同,所以交由具体的线程池来实现。
线程池种类
阿里规范我们用TreadPoolExecutor
newFixedThreadPool:创建固定线程数的线程池。核心线程数等于最大线程数,不存在空闲线程,keepAliveTime为0。
newSingleThreadExecutor:创建单线程的线程池,核心线程数和最大线程数都为1,相当于串行执行。
newCachedThreadPool:核心线程数为0,最大线程数为Integer.MAX_VALUE,是一个高度可伸缩的线程池。存在OOM风险。keepAliveTime为60,工作线程处于空闲状态超过keepAliveTime会回收线程。

newWorkStealingPool:JDK8引入,创建持有足够线程的线程池支持给定的并行度,并通过使用多个队列减少竞争。

禁止直接使用Executors创建线程池原因:
Executors.newCachedThreadPool和Executors.newScheduledThreadPool两个方法最大线程数为Integer.MAX_VALUE,如果达到上限,没有任务服务器可以继续工作,肯定会抛出OOM异常。
Executors.newSingleThreadExecutor和Executors.newFixedThreadPool两个方法的workQueue参数为new LinkedBlockingQueue(),容量为Integer.MAX_VALUE,如果瞬间请求非常大,会有OOM风险。

以上5个核心方法除Executors.newWorkStealingPool方法之外,其他方法都有OOM风险。
具体实现线程池
Plain Text
复制代码

/**

  • 线程池执行耗时的操作 不要加大核心线程和最大线程数
    */
    private static final ExecutorService EXECUTOR_SERVICE = new ThreadPoolExecutor(1,
    1,
    120,
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(1),
    new RegionCenterUtilThreadFactory(“InitTemplateUtilThreadPool”),
    new ThreadPoolExecutor.DiscardOldestPolicy());

/**

  • 线程池工厂实现类
    */
    public static class RegionCenterUtilThreadFactory implements ThreadFactory {
    private final String name;

    RegionCenterUtilThreadFactory(String name) {
    this.name = name;
    }

    @Override
    public Thread newThread(Runnable r) {
    return new Thread(r, this.name);
    }
    }
    Plain Text
    复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    private void generateMerchantShopTemplate(Long merchantId, Long userId, String userName) {
    BaseReq base = new BaseReq();
    base.setMerchantId(merchantId);
    base.setUserId(userId);
    base.setUserName(userName);
    log.info(“initializeTemplateDataUtil start”);
    try {
    EXECUTOR_SERVICE.execute(() -> {
    // 新建商户时 初始化标准
    Boolean data = this.collectionTemplateV2RemoteService.saveMerchantInitTemplate(base).getData();
    log.info("==data:{}", data);
    });
    } catch (Exception e) {
    log.error(“initializeTemplateDataUtil fail: e =”, e);
    }
    log.info(“initializeTemplateDataUtil finfish”);
    }
    java线程池详解
    java线程池详解_不会秃头的小齐的博客-CSDN博客
    线程池详解
    7000字+24张图带你彻底弄懂线程池_Java烟雨的博客-CSDN博客
    Nginx
    Nginx的安装配置教程 https://blog.csdn.net/BraveZhouzhou/article/details/125913797

原理底层计划--线程池原理和常见的问题相关推荐

  1. python线程池原理_Python定时器线程池原理详解

    这篇文章主要介绍了Python定时器线程池原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 定时器执行循环任务: 知识储备 Timer(int ...

  2. 一文搞懂线程池原理——Executor框架详解

    文章目录 1 使用线程池的好处 2 Executor 框架 2.1 Executor 框架结构 2.2 Executor 框架使用示意图 2.3 Executor 框架成员 2.3.1 Executo ...

  3. java并发包线程池原理分析锁的深度化

    java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素 ...

  4. 【5】线程池原理分析

    目录 知识点1:并发包 1.(计数器)CountDownLatch 2.(屏障)CyclicBarrier 3.(计数信号量)Semaphore (1)案例 4.并发队列 5.阻塞队列与非阻塞队 (1 ...

  5. 一文弄懂Java中线程池原理

    在工作中,我们经常使用线程池,但是你真的了解线程池的原理吗?同时,线程池工作原理和底层实现原理也是面试经常问的考题,所以,今天我们一起聊聊线程池的原理吧. 为什么要用线程池 使用线程池主要有以下三个原 ...

  6. Java 线程池原理总结

    Java 线程池原理总结 (一)什么是线程池 线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它 ...

  7. java 线程池原理分析

    一.为什么使用线程池 1.降低资源消耗,减少线程创建和销毁次数,每个工作线程可以重复利用,执行多个任务 2.可根据系统承受能力,调整工作线程的数目,防止消耗过多的内存 二.java 线程池使用 Exe ...

  8. JAVA线程池原理以及几种线程池类型介绍

    在什么情况下使用线程池? 1.单个任务处理的时间比较短      2.将需处理的任务的数量大 使用线程池的好处: 1.减少在创建和销毁线程上所花的时间以及系统资源的开销      2.如不使用线程池, ...

  9. 只需这篇文章java线程池原理便懂了!♥♥

    模拟Java线程池运行原理 在了解线程池之前,我们先来谈谈线程的状态转换 线程常用方法及种类 线程池实现原理和线程池概念 四种线程池 线程池的组成 代码 自定义线程池功能简单 在了解线程池之前,我们先 ...

最新文章

  1. pandas任取dataframe中的一个或者多个数据行(head、tail、loc、iloc),将抽取到的一个或者多个数据行复制N次形成新的dataframe
  2. vivado----fpga硬件调试 (二)----mark_debug
  3. 在html中标记中可以嵌套标记,如何在LESS CSS嵌套类中指定html标记?
  4. C++中WSAAsyncSelect模型的用法例程
  5. 微服务pact测试框架_消费者驱动的Pact和Spring Boot测试
  6. 第五章· MySQL数据类型
  7. C# Windows Phone 8 WP8 开发,将WebClient的DownloadStringCompleted事件改成非同步的awiat方法。...
  8. 关于linux系统端口查看和占用的解决方案
  9. 问题五十九:怎么求一元六次方程在区间内的所有不相等的实根(3)——修正一个问题
  10. 一起谈.NET技术,WPF的消息机制(一)- 让应用程序动起来
  11. 前台传参到后台出现中文乱码问题
  12. java输出流文件_Java文件输入输出流(封装类)
  13. 【宠物领养系统项目】(附源码)
  14. 闪电html编辑器,闪电pdf编辑器
  15. linux iometer安装教程,IOmeter 2010在Linux x64下安装
  16. 新cBSS敏捷发布实践
  17. 集成32位/64位:Office 2010简体中文专业增强版下载+最新密钥
  18. ARC093 F - Dark Horse
  19. ミルシャ / 人鱼枪
  20. 阿里算法实习生面试回忆

热门文章

  1. 端口映射--自己主机做服务器
  2. 增强现实 北京触景无限的AR产品,国外的WiKiTude SDK
  3. 快速上手用Taro框架搭建一个微信小程序
  4. java怎么根据新历算农历_Java给定公历日期计算相应农历/阴历日期
  5. 微信图片上传检测图片是否含有违禁内容
  6. 高考经济日趋火爆,怎样才干做到“接地气“?
  7. 从全职高手开始的系统_第一章 全职高手系统
  8. 这种格式的梯形面积算法应该怎么写
  9. 转置卷积(反卷积)为什么姓转置
  10. 计算机组成原理考研试题百度云,计算机组成原理考研题库