线程池基本知识
来自B站狂神说Java,此为我的学习笔记。
写入:如果队列满了,就必须阻塞等待;
读取:如果队列空的,就必须阻塞生产;
--不得不阻塞
阻塞队列
什么时候我们会使用阻塞队列:
1.多线程
2.线程池
学会使用队列
添加,移除
四组API
1.抛出异常;
2.不会抛出异常;
3.阻塞等待;
4.超时等待;
四组API
方式 | 抛出异常 | 有返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer | put | offer(,) |
移除 | remove | poll | take | take(,) |
判断队列首部 | element | peek | ||
线程池
池化技术:事先准备一些资源,有人要用,就来池里拿,用完之后,还回来;
线程池的好处:
- 降低资源的消耗;
- 提高响应的速度;
- 方便管理;
- 总结:线程复用,可以控制最大并发数,管理线程;
线程池:三大方法、七大参数、四种拒绝策略;
三大方法:
// ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程 // ExecutorService threadPool = Executors.newFixedThreadPool(5); //创建固定的线程池大小 ExecutorService threadPool = Executors.newCachedThreadPool(); //可伸缩,遇强则强,遇弱则弱
七大参数:
//源码分析 public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } //--------------------------------------------------------------------------------- public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; } //参数: /* 1.int corePoolSize 核心线程池大小; 2.int maximumPoolSize 最大核心线程池大小; 3.long keepAliveTime 存活时间; 4.TimeUnit unit 超时单位; 5.BlockingQueue<Runnable> workQueue 阻塞队列; 6.ThreadFactory threadFactory 线程工厂,创建线程; 7.RejectedExecutionHandler handler 拒绝策略; */
四种拒绝策略:
AbortPolicy
;DiscardPolicy
;DiscardOldestPolicy
;CallerRunsPolicy
;
线程池所能承载的最大线程:线程核心池最大数量+阻塞队列的数量;
如果超过线程池所能承载的线程数量,就会被拒绝策略接收;
代码示例:
package com.company.juc.pool; import java.util.concurrent.*; import java.util.concurrent.ThreadPoolExecutor.AbortPolicy; /** * @author * @create 2021/10/10 **/ public class Demo1 { public static void main(String[] args) { //工具类,三大方法 // ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程 // ExecutorService threadPool1 = Executors.newFixedThreadPool(5); //创建固定的线程池大小 // ExecutorService threadPool2 = Executors.newCachedThreadPool(); //可伸缩,遇强则强,遇弱则弱 //创建阻塞队列; LinkedBlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(3); ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 2, 5, 2, TimeUnit.SECONDS, blockingQueue, Executors.defaultThreadFactory(), //new ThreadPoolExecutor.AbortPolicy() //如果银行还有人进来,不处理这个人的,抛出异常; // new ThreadPoolExecutor.CallerRunsPolicy() //那来的去哪里 ,哪里调用的,返回到那个线程; // new ThreadPoolExecutor.DiscardPolicy() //不进行处理,丢弃任务,不抛出异常; new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试和最早的去竞争,如果没有拿到,丢弃任务,不会抛出异常 ); //之前创建线程的方式 /* for (int i = 0; i < 10; i++) { new Thread().start(); }*/ //使用线程池创建线程的方式 try { for (int i = 0; i < 10; i++) { threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+"->"+"被创建!"); }); } }catch (Exception e){ e.printStackTrace(); } finally { //创建线程池之后,需要关闭 threadPool.shutdown(); } } }
阿里巴巴开发手册规定:
- 线程池不允许使用Executors去创建,而是通过
ThreadPoolExecutor
的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险;FixedThreadPool
和SingleThreadPool
允许的请求队列长度为Interger.MAX_VALUE
可能会堆积大量的请求,从而导致OOM;CachedThreadPool
和ScheduledThreadPool
允许的创建线程的数量为Interger.MAX_VALUE
可能会创建大量的线程,从而导致OOM;
程序的运行本质:占用系统的资源,优化CPU资源的使用;
线程池、连接池、对象池;
- 最小值;
- 最大值;