线程池的自我介绍

思考

如果不使用线程池,我们可能需要每次任务都新开一个线程来处理。

  • 如果只有一个线程
public class oneThread {public static void main(String[] args) {Thread thread = new Thread(new Task());thread.start();}static class Task implements Runnable{@Overridepublic void run() {System.out.println("正在执行任务");}}
}//正在执行任务
  • 如果有十个线程
public static void main(String[] args) {for (int i = 0; i < 10; i++) {Thread thread = new Thread(new Task());thread.start();}}static class Task implements Runnable{@Overridepublic void run() {System.out.println("正在执行任务");}}/*正在执行任务正在执行任务正在执行任务正在执行任务正在执行任务正在执行任务正在执行任务正在执行任务正在执行任务正在执行任务
*/
  • 如果任务数量上升到1000个怎么办?
        for (int i = 0; i < 1000; i++) {Thread thread = new Thread(new Task());thread.start();}

这样的开销太大,会产生反复创建并销毁线程所带来的开销问题。在大量创建线程的时候内存消耗会很大,在线程运行完毕之后需要回收,又会给垃圾回收器带来压力。操作系统创建的线程是有上限的,一直不停创建线程,可能会超过上限导致出错,报出OOM异常。

为什么要使用线程池

  • 返回创建线程开销大
  • 过多的线程会占用太多的内存

创建和停止线程池

线程池构造函数的参数

corePoolSize

线程池在完成初始化后,默认情况下,线程池中并没有任务线程,线程池会等待任务带来时,在创建新线程去执行任务

maxPoolSize

线程池有可能会在核心线程池的基础上,额外创建一些线程,但是这些线程数会有一个上限,这就是最大量maxPoolSize

添加线程的规则
  • 如果线程数小于corePoolSize.即使其他工作的线程处于空闲状态,也会创建一个新线程来运行新任务
  • 如果线程数等于corePoolSize但少于maxPoolSize,则将任务放入队列
  • 如果队列已满,并且线程数小于maxPoolSize,则创建一个新线程来运行任务
  • 如果队列已满,并且线程数大于或等于maxPoolSize,则拒绝该任务

keepAliveTime

如果线程池当前的线程多于corePoolSize,那么如果多于的线程空闲时间超过keepAliveTime,他们就会被终止

threadFactory

新的线程是由ThreadFactory创建的,默认使用Executors.defaultThreadFactory(),创建出来的线程都在同一个线程组,拥有相同的优先级并且都不是守护线程。如果自己指定ThreadFactory,那么就可以改变线程名,线程组,优先级,是否是守护线程等

workQueue

有3种常见的队列类型

  • 直接交换

SynchronousQueue

任务不会太多,只是通过队列对任务进行简单的中转,然后交到线程池去执行。

  • 无界队列

LinkedBlockingQueue

最大值为Integer.MAX_VALUE,如果任务的处理速度小于任务提交的速度,会造成队列中任务的堆积,甚至OOM异常

  • 有界队列

ArrayBlockingQueue

需要设置任务的数量

线程需要手动创建还是自动创建

手动创建更好,因为这样可以让我们更加明确线程的运行规律,避免资源耗尽的风险

自动创建可能带来的问题
newFixedThreadPool
public static void main(String[] args) {ExecutorService service = Executors.newFixedThreadPool(4);for (int i = 0; i < 1000; i++) {service.execute(new Task());}}static class Task implements Runnable{@Overridepublic void run() {try {Thread.sleep(500);System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}}
    public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}

由于传进去的LinkedBlockingQueue是没有容量上限的,所有当请求数越来越多,并且无法及时处理完毕的时候,也就是请求推挤的时候,会容易造成占用大量的内存,可能会导致OOM

OOM异常演示

//-Xmx10m -Xms10mprivate  static ExecutorService service = Executors.newFixedThreadPool(1);public static void main(String[] args) {for (int i = 0; i < Integer.MAX_VALUE; i++) {service.execute(new Task());}}class Task implements Runnable{@Overridepublic void run() {try {Thread.sleep(500000000);System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}
}//Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
newSingleThreadExecutor

跟newFixedThreadPool 线程池一样,任务堆积时可能出现OOM异常

private  static ExecutorService service = Executors.newSingleThreadExecutor();public static void main(String[] args) {for (int i = 0; i < Integer.MAX_VALUE; i++) {service.execute(new Task());}}class Task implements Runnable{@Overridepublic void run() {try {Thread.sleep(500);System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}
}
//pool-1-thread-1
//pool-1-thread-1
//pool-1-thread-1
//... 只有一个线程
    public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}
newCachedThreadPool

可缓存线程池

特点:无界的线程池,具有自动回收多余线程的功能

 private  static ExecutorService service = Executors.newCachedThreadPool();public static void main(String[] args) {for (int i = 0; i < Integer.MAX_VALUE; i++) {service.execute(new Task());}}class Task implements Runnable{@Overridepublic void run() {try {Thread.sleep(500);System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}
}//注意控制台打印
//pool-1-thread-423
//...线程数在一直增加
//pool-1-thread-3585
    public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}

这里的弊端在与第二个参数maxPoolSize被设置为了Integer.MAX_VALUE,这可能会创建数量非常多的线程,甚至导致OOM异常

newSingleThreadExecutor
    private static ScheduledExecutorService service = Executors.newScheduledThreadPool(1);public static void main(String[] args) {//几秒后打印出内容service.schedule(new Task(),3, TimeUnit.SECONDS);//周期性执行任务  第一次是线程启动后3秒执行  以后每隔一秒执行任务service.scheduleAtFixedRate(new Task(),3,1,TimeUnit.SECONDS);}class Task implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName());System.out.println("正在执行任务");}
}
正确创建线程的方法
  • 根据不同的任务场景,自己设置线程池的参数,比如我们的内存有多大,我们想给线程去什么名字,任务拒绝后应该怎么记录日志。
线程池里的线程数量设置为多少比较合适
  • CPU密集型(加密等)

    最佳的线程为CPU核心数的1-2倍

  • IO型(读写数据库,文件,网络读写)

    最佳线程数一般会大于cpu数量核心数的很多倍。

  • 通用公式

    线程数 = CPU核心数 * (1+平均等待时间/平均工作时间)

停止线程池的正确方法
shutdown

初始化关闭线程操作,需要等待线程正在执行的任务以及队列中已经存在的任务都执行完毕后,才会关闭线程池

    private static ExecutorService service = Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {service.execute(new Task());}Thread.sleep(1500); //主线程睡眠1500毫秒service.shutdown();//shutdown()并不会立马停止线程池service.execute(new Task());//在执行shutdown() 执行之后,继续添加任务到线程池。//会报RejectedExecutionException异常}class Task implements Runnable{@Overridepublic void run() {try {Thread.sleep(1000);System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}
}//运行代码
isShutdown

判断线程池是否处于shutdown()状态。并不代表线程池已经停止

   private static ExecutorService service = Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {service.execute(new Task());}Thread.sleep(1500);System.out.println(service.isShutdown()); //falseservice.shutdown();System.out.println(service.isShutdown()); //true}class Task implements Runnable{@Overridepublic void run() {try {Thread.sleep(1000);System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}
}
isTerminated

线程池真正停止工作

 private static ExecutorService service = Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 100; i++) {service.execute(new Task());}Thread.sleep(100);System.out.println(service.isShutdown()); //falseservice.shutdown();System.out.println(service.isShutdown()); //trueSystem.out.println("----------");System.out.println(service.isTerminated()); //falseThread.sleep(10000);System.out.println(service.isTerminated()); //true}class Task implements Runnable{@Overridepublic void run() {try {Thread.sleep(1000);System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}
}
awaitTermination

等待一定时间,判断线程时候结束。如果结束返回true, 没有则返回false

  private static ExecutorService service = Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 100; i++) {service.execute(new Task());}System.out.println(service.awaitTermination(1,TimeUnit.SECONDS)); //falseSystem.out.println(service.awaitTermination(10,TimeUnit.SECONDS)); //true}class Task implements Runnable{@Overridepublic void run() {try {Thread.sleep(1000);System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}
}
shutdownNow

会中断正在运行的线程,并且返回队列中的线程

    private static ExecutorService service = Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 100; i++) {service.execute(new Task());}Thread.sleep(500);List<Runnable> list = service.shutdownNow();System.out.println(list);}class Task implements Runnable{@Overridepublic void run() {try {Thread.sleep(500);System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName()+"中断了");}}
}

任务的拒绝

拒绝的时机
  • 当Executor关闭时,提交新任务会被拒绝

  • 当Exector对最大线程和工作队列容量使用有限边界并且饱和时

4中拒绝的策略
  • AbortPlicy

    直接拒绝,抛出异常

  • DiscardPolicy

    直接丢弃

  • DiscardOldestPolicy

丢弃最老的任务

  • CallerRunsPolicy

    提交任务的线程来执行

    优点:利用提交的线程的执行任务,会降低线程池接收任务的速度,让线程池有时间来执行其他任务。

线程池的实现原理,源码分析

线程的组成部分
  • 线程池管理器

    来管理线程的创建,关闭操作

  • 工作线程

    创建出来执行任务的线程

  • 任务队列

    存放任务的队列

  • 任务

    要执行的任务

线程池,ThreadPoolExecutor,ExecutorService,Executors,Executor的关系

Executor

顶层的接口,只有一个方法

public interface Executor {void execute(Runnable command);
}
ExecutorService

继承自executor, 扩展了一些管理线程池的方法

Executors

工具类,方便创建线程池。

ThreadPoolExecutor

线程池

线程池的状态
running

接收新任务并处理排队任务

shutdown

不接受新任务,但处理排队任务

stop

不接收新任务,不处理排队的任务,中断正在执行的任务。

tidying

所有的任务都已终止,workcount为0,线程切换到tidying状态,并将运行terminate()

terminate

terminate() 运行完成

线程池的注意点
  • 避免任务的堆积
  • 避免线程数的过度增加
  • 排查线程泄露

Executors线程池知识点总结相关推荐

  1. Executors线程池关闭时间计算

    Executors线程池关闭时间计算 学习了:http://blog.csdn.net/wo541075754/article/details/51564359 https://www.cnblogs ...

  2. Java Executor源码解析(7)—Executors线程池工厂以及四大内置线程池

    详细介绍了Executors线程池工具类的使用,以及四大内置线程池. 系列文章: Java Executor源码解析(1)-Executor执行框架的概述 Java Executor源码解析(2)-T ...

  3. Java Executors(线程池)

    Sun在Java5中,对 Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利.为了编写高效稳定可靠的多线程程 ...

  4. Executors 线程池的7个参数详解

    线程池创建有7个参数 下面为构造函数 /** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameter ...

  5. Executors线程池详解(全)

    开场白 构造一个线程池为什么需要几个参数?如果避免线程池出现OOM? Runnable 和 Callable 的区别是什么?本文将对这些问题一一解答,同时还将给出使用线程池的常见场景和代码片段. 基础 ...

  6. java多线程——Executors线程池的四种用法简单解析

    1.Executors.newFixedThreadPool(5)    是创建一个线程池,池子里面有5个线程,任务数多余5个时,超出的任务队列中排队等候执行 2.Executors.newCache ...

  7. DownloadManager+NumberProgressBar+Executors线程池实现多并发下载APK安装

    在github上面苦苦寻找都木有找到有关Android自带下载器DownloadManager多并发下载的,于是就决定自己试试写一个. 先上个图: 我的上篇文章 http://blog.csdn.ne ...

  8. Executors线程池

    //工作中只用这种//最大线程数定义 1.CPU密集型 几核就是几 可保证CPU效率最高Runtime.getRuntime().availableProcessors();//获取CPU核数//IO ...

  9. java executors 详解_线程池Executors详解

    为什么要用线程池呢? 一是减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务; 二是可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为因为消耗过多的内存,而把服务器累 ...

最新文章

  1. Linux 创建yum源和软件仓库实例
  2. 为打击网络喷子 Instagram禁评论攻击
  3. 杭电oj1072java实现bfs
  4. python os sys_python os模块sys模块常用方法
  5. 程序员谈薪技巧公开,这样谈比预期高30%
  6. 2013-9 OWASP论坛
  7. php 判断当前栏目高亮,Phpcms V9采用if语句判断当前栏目高亮、判断分类信息是否过期...
  8. java面向对象高级分层实例_实体类
  9. jdk8读取文件_JDK 7和JDK 8中大行读取速度较慢的原因
  10. Web Service初探
  11. js右下角广告[兼容]
  12. Html数组下标访问帧,javaScript array(数组)使用字符串作为数组下标的方法
  13. 《三国演义》分章节梗概
  14. 一个完整的机器学习模型的流程
  15. windows10电脑发现不了网络计算机,Win10电脑无法开启网络发现怎么解决?
  16. 计算机的硬件和价格,简述台式电脑的各硬件价格占比
  17. 内存系列三:内存初始化浅析
  18. '/0'和/0的区别
  19. Keil5.15使用GCC编译器编译STM32工程
  20. 【Oracle数据库驱动架包 ojdbc5.jar ojdbc6.jar】

热门文章

  1. 伪造公司印章需要判刑几年
  2. git同步个人仓库与主仓库代码
  3. 达观资讯推荐系统助力打造更懂用户的新闻客户端
  4. 物联网成全球热点 七大看点全面呈现行业近况
  5. 在函数后面加const
  6. 一文带你了解场效应管
  7. 负债20万,一个月工资四千…怎么办?
  8. 关于即来即停app的功能
  9. img标签中的srcset属性有什么用?
  10. 海思AI芯片(Hi35XX): 图像jpg转.bgr升级版