Java中的线程池
一:什么是线程池
线程池就是用来管理和复用线程的工具,它可以减少线程的创建和销毁开销。
举个例子:
现有一个建筑公司,
它现在需要做一个A项目,该项目需要5个人,该项目需要招聘5个临时工来做这个工作,做完工作后结算工钱走人!
过了一段时间,
公司又做一个B项目,该项目需要8个人,那么该项目还需招聘8个临时来做这个工作,做完工作后结算工钱走人!
这样非常麻烦 >_>!!
因此现在就开辟了一个线程池,把5个临时工放入线程池中,让其变成了该公式的员工,其设置为核心线程池为5,即其核心工人为5;
之后如果项目需要的人多与5个,就再找临时工,如果需要的人少于5个就,从核心工人中选;
二:如何创建一个线程池
//创建一个线程池 ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,factory,handler);
其中线程池中又7个参数,其中分别为:
核心线程数,最大线程数,保持存活的时间,设置时间的单位,阻塞队列,线程池中的线程工厂,拒绝策略处理器;
三:创建一个线程池,并用execute/submit方法执行任务
1.线程池的执行流程
第一步:创建线程池;
第二步:用execute方法执行任务
第三步:线程执行完毕后,线程并不会立即销毁,而是继续保持在池中等待下一个任务
第四步:当线程空闲时间超出指定时间,且当前线程数量大于核心线程数时,线程会被回收
2.例子(用execute方法执行任务):
public static void main(String[] args) { //核心线程数 int corePoolSize = 5; //最大线程数 int maximumPoolSize = 10; //保持存活的时间 -- 刨除去核心线程数,不超过最大的线程数,其他多余的临时线程的存活时间为1s //也就是说,线程数超过核心的线程数,空闲的时间超过keepAliveTime,就会销毁非核心线程 long keepAliveTime = 1; //设置时间的单位,为s TimeUnit unit =TimeUnit.SECONDS; //阻塞队列(现有线程数多余最大的线程数,就是多余的线程,就需要阻塞队列,让其等待) BlockingDeque<Runnable> workQueue =new LinkedBlockingDeque<>(); //线程池中的线程工厂 -- 默认创建我们的线程 ThreadFactory factory=Executors.defaultThreadFactory(); //拒绝策略处理器 ,当超过线程池的最大线程数,并且,其阻塞队列也放不下的时候,会用拒绝策略处理器,进行拒绝 ThreadPoolExecutor.AbortPolicy handler =new ThreadPoolExecutor.AbortPolicy(); //创建一个线程池 ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor( corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,factory,handler ); //使用线程池 for (int i = 0; i < 30; i++) { poolExecutor.execute( ()->{ System.out.println("当前的线程为"+Thread.currentThread().getName()+"正在执行"); } ); } poolExecutor.shutdown(); }
运行结果:
3.例子(并用submit方法执行任务):
public static void main(String[] args) { //核心线程数 int corePoolSize = 5; //最大线程数 int maximumPoolSize = 10; //保持存活的时间 -- 刨除去核心线程数,不超过最大的线程数,其他多余的临时线程的存活时间为1s //也就是说,线程数超过核心的线程数,空闲的时间超过keepAliveTime,就会销毁非核心线程 long keepAliveTime = 1; //设置时间的单位,为s TimeUnit unit =TimeUnit.SECONDS; //阻塞队列(现有线程数多余最大的线程数,就是多余的线程,就需要阻塞队列,让其等待) BlockingDeque<Runnable> workQueue =new LinkedBlockingDeque<>(); //线程池中的线程工厂 -- 默认创建我们的线程 ThreadFactory factory=Executors.defaultThreadFactory(); //拒绝策略处理器 ,当超过线程池的最大线程数,并且,其阻塞队列也放不下的时候,会用拒绝策略处理器,进行拒绝 ThreadPoolExecutor.AbortPolicy handler =new ThreadPoolExecutor.AbortPolicy(); //创建一个线程池 ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor( corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,factory,handler ); //使用线程池 List<Object> f =new ArrayList<>(); for (int i = 0; i < 30; i++) { Future<?> future = poolExecutor.submit( ()->{ String s = "当前的线程为" + Thread.currentThread().getName() + "正在执行"; System.out.println(s); return s; } ); try { Object o = future.get(); f.add(o); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } poolExecutor.shutdown(); System.out.println("+++++++++++++++"); System.out.println("f = " + f); }
运行结果
四:submit与execute的区别
execute 方法没有返回值,适用于不关心结果和异常的简单任务;
submit 有返回值,适用于需要获取结果或处理异常的场景。
五:线程池有几种阻塞队列
有5种
有界队列 ArrayBlockingQueue;
无界队列 LinkedBlockingQueue;
优先级队列 PriorityBlockingQueue;
延迟队列 DelayQueue;
同步队列 SynchronousQueue
1.ArrayBlockingQueue:一个先进先出的有界队列,底层是一个数组,一般大小固定的线程池都用它;
2.LinkedBlockingQueue:底层是一个链表,不指定大小,默认为Integer.MAX_VALUE,相当于一个无界队列;
3.PriorityBlockingQueue:一个支持优先级排序的无界队列;
4. DelayQueue:和优先级队列相似,是由二叉树实现的无界优先级阻塞队列;
5.SynchronousQueue:每一个插入操作必须等待一个线程移除操作,
任何一个移除操作必须等待另一个线程的插入操作;
六:线程池该如何关闭
可以调用线程池的shutdown或shutdownNow方法来关闭线程池。
1.shutdown 不会立即停止线程池,而是等待所有任务执行完毕后再关闭线程池
2.①shutdownNow 会尝试通过一系列动作来停止线程池,包括停止接收外部提交的任务、忽略队列里等待的任务、尝试将正在跑的任务 interrupt 中断;
②shutdownNow 不会真正终止正在运行的任务,只是给任务线程发送 interrupt 信号,任务是否能真正终止取决于线程是否响应 InterruptedException