个人注册公司查询,郑州seo博客,大连公司网站建设,网站建设基础知识文章目录 1、线程池2、分类3、线程池的使用4、工作流程5、拒绝策略6、线程池的七个参数7、自定义线程池8、什么时候考虑使用线程池#xff1f; 1、线程池
线程池和数据库连接池的理念很相似#xff0c;对于数据库连接池#xff1a;普通的连接数据库是建立一个JDBC连接… 文章目录 1、线程池2、分类3、线程池的使用4、工作流程5、拒绝策略6、线程池的七个参数7、自定义线程池8、什么时候考虑使用线程池 1、线程池
线程池和数据库连接池的理念很相似对于数据库连接池普通的连接数据库是建立一个JDBC连接执行完sql之后就会关闭即销毁connection对象再次连接还需要重复上述步骤。当与数据库交互频繁时这种模式会严重影响程序的性能因此有了数据库连接池。对应到线程池thread pool就是线程池里维护着多个线程等待监督管理者分配执行任务。线程池带来的好处就是
降低资源消耗降低避免频繁创建和销毁线程的代价提高响应速度任务达到时不用再等待创建线程线程管理方便线程过多调度开销大用线程池可防止过分调度且可以做统一的监控、分配、调优
关于线程切换的例子10 年前单核 CPU 电脑假的多线程像马戏团小丑玩多个球CPU 需要来回切换。 现在是多核电脑多个线程各自跑在独立的 CPU 上不用频繁切换效率高。
2、分类
Java 中的线程池是通过 Executor 框架实现的该框架中用到了 Executors工具类、ExecutorsExecutorService、ThreadPoolExecutor这几个类
线程池有以下几类
一池N线程Executors.newFixedThreadPool(int num)一池一线程Executors.newSingleThreadExecutor()可扩容池根据需求创建一定数量的线程遇强则强Executors.newCachedThreadPool()
3、线程池的使用
创建线程池对象调用execute方法提交任务
public class ThreadPoolDemo {public static void main(String[] args) {//一池五线程ExecutorService threadPool Executors.newFixedThreadPool(3);//一池1线程ExecutorService threadPool1 Executors.newSingleThreadExecutor();//一池可扩容线程ExecutorService threadPool2 Executors.newCachedThreadPool();//提交10次任务到线程池try{for (int i 1; i 20; i) {//提交任务到另一线程线程池中的threadPool2.execute(() - {System.out.println(Thread.currentThread().getName() 线程正在办理业务);});}}catch(Exception e){e.printStackTrace();}finally{threadPool2.shutdown();}}
}
以可扩容线程池为例 4、工作流程 如图此时常驻线程数为2最大线程数为5阻塞队列长度为3黑点此时来了两个任务1和2 ⇒ 直接常驻线程 ⇒ 那两个任务还执行完又来了几个任务3、4、5 ⇒ 这时不是直接上最大线程而是进入阻塞队列 ⇒ 此时又来了三个人6、7、8 ⇒ 发现阻塞队列也满了那就开启最大线程处理6、7、8的业务 注意新开的线程不是去处理阻塞队列了阻塞队列的3、4、5还是在队列中继续等待 ⇒ 此时又来了一个任务9 ⇒ 走拒绝策略
注意这几点
ExecutorService pool Executors.newSingleThreadExecutor();执行上面这句并不会创建线程而是执行pool.execute方法提交任务时才创建常驻线程用完了再来任务不是直接按最大线程数启动新线程而是阻塞队列阻塞队列满了以后按最大线程数启动新线程且新线程处理的不是阻塞队列里的任务
看下源码从提交任务的execute方法打断点进入execute方法 5、拒绝策略
阻塞队列和最大线程数量都用完后走拒绝策略JDK内置的拒绝策略有
AbortPoligy(默认)直接抛出RejectedExecutionExption异常阻止系统正常运行CallerRunsPoliy既不会抛弃任务也不会抛出异常而是将某些任务回退到调用者谁让你来的你找谁去DiscardOldestPoliy抛弃阻塞队列中等待最久的任务然后把当前任务加入队列中DiscardPolicy默默地丢弃无法处理的任务不予任何处理也不抛出异常如果允许任务丢失这是最好的一种策略
6、线程池的七个参数
查看源码可以发现不管是三种线程池中的哪种最后都是return new ThreadPoolExecutor关于ThreadPoolExecutor类 int corePoolSize常驻线程数量int maximumPoolSize最大线程数量long keepAliveTime线程存活时间线程多长时间没被使用就关闭TimeUnit unit存活时间的单位BlockingQueue workQueue常驻线程用完了再来请求线程进入阻塞队列ThreadFactory threadFactory线程工厂RejectedExecutionHandler handler拒绝策略
以银行为例对比银行大厅一共有10个窗口最大线程数量但平时一般只开5个常驻线程数量某天办理业务的人很多5个窗口不够用其余人来了就先在大厅椅子上坐着等阻塞队列结果椅子坐满了还有人陆续来于是10个窗口全开还来很多人那就只能告诉新来的今天轮不到你办了拒绝策略。
7、自定义线程池
Executors工具类可以创建三种线程池但通常自定义线程池是因为Executors返回的线程池对象有以下两个问题
对于FixedThreadPool和SingleThreadPool代码底层用的阻塞队列是LinkedBlockingQueue类型的队列长度为Integer.MAX_VALUE可能堆积大量请求导致OOM对于CachedThreadPool其源码中写的最大线程数量为Integer.MAX_VALUE创建大量线程调度难度大且会OOM
public class ThreadPoolDemo2 {public static void main(String[] args) {ExecutorService threadPool new ThreadPoolExecutor(2, //常驻或核心线程数5, //最大线程数2L,TimeUnit.SECONDS,new ArrayBlockingQueue(3), //阻塞队列Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() //拒绝策略);try {for (int i 1; i 20; i) {threadPool.execute(() - {System.out.println(Thread.currentThread().getName() 线程正在办理业务);});}} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();}}
}8、什么时候考虑使用线程池
到这儿线程池的作用、分类、底层代码逻辑、参数与策略的问题基本清晰那什么时候考虑去使用线程池呢 线程池适合处理耗时任务可以充分使用目前服务器的硬件资源加快处理速度。更确切的说是
单个任务处理时间比较短但需要处理的任务的数量大
此时如果不使用线程池随意启动许多线程容易导致系统因创建大量线程而OOM且过渡调度过渡切换。比如工作中遇到一个excel数据转换后批量写入库里就可拆为一批批的小任务去insert。还有帖子说需要限制并发执行的任务数量时也可以用线程池这儿我先想到的反而是Semaphore信号灯这个JUC辅助类。