zoukankan      html  css  js  c++  java
  • java线程池

    多线程实践分页查询

    多线程是个好东西,用好了可以对性能提升很大。之前写了一分页查询的页面,大致的模型是这样的:

    希望对A进行分页查询,但是又想把B,C,D,E的信息也带出来,最开始是没有用多线程,强行查询除了A的List,然后遍历A中的元素,依次查询出对应的B,C,D和E。当时看了一下请求时间大概有700~800ms,确实是肉眼可见的慢。然后想着不行,还是用多线程优化一下吧,然后把查询和A关联的表的信息任务划分一下,交给不同的线程处理,组装数据完毕之后,主线程把最后的List返回到前台。最后的查询时间降低到160ms左右。

    为什么需要用线程池

    如果不使用线程池的话,大概率使用线程的方式是这样的:

    每次使用线程的时候,都要出创建一个线程的实例,最后用完了之后还要销毁它,线程在Linux也叫轻量级进行,虽然有个轻量级前缀,但是它的创建和销毁也是比较消耗系统资源的。想象一下一次创建几十个线程,使用完了在销毁;然后在创建十几个线程,再销毁,这个性能损耗就很客观了(这个暂时是吓唬你的,因为我还没有测过)。除了创建和销毁线程的开销之外,活动的线程也消耗系统资源。在一个 JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或"切换过度"。

    线程池的好处:

    • 实现了线程的重用,降低因为重复创建线程而带来的开销
    • 任务来到的时候马上就有线程可用,消除了线程创建所带来的延迟,响应更快

    这种设计思想在很多地方是可以见到的:

    • Integer的享元设计模式
    • 数据库的连接池

    都是基于性能的考量。

    java的线程和操作系统中进程的关系?

    这个先留着......

    java中的线程池

    已经看博客的时候经常会看到java中有四种线程池:

    但是今天去看了下java.util.concurrent.Executors的源码,发现了好像不止这四种呀,然后网上看了下有七种!

    • newCachedThreadPool :创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    • newFixedThreadPool:创建一个固定数目的、可重用的线程池。
    • newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。
    • newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
    • newSingleThreadScheduledExcutor:创建一个单例线程池,定期或延时执行任务。
    • newWorkStealingPool:创建持有足够线程的线程池来支持给定的并行级别,并通过使用多个队列,减少竞争,它需要穿一个并行级别的参数,如果不传,则被设定为默认的CPU数量。
    • ForkJoinPool:支持大任务分解成小任务的线程池,这是Java8新增线程池,通常配合ForkJoinTask接口的子类RecursiveAction或RecursiveTask使用。

    线程池的创建

    其中有好几种都是通过类java.util.concurrent.ThreadPoolExecutor创建出来的,而它又有好几种构造方法,这里挑一个典型的来分析一下:

    1 public ThreadPoolExecutor(int corePoolSize,
    2                           int maximumPoolSize,
    3                           long keepAliveTime,
    4                           TimeUnit unit,
    5                           BlockingQueue<Runnable> workQueue) {
    6     this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
    7          Executors.defaultThreadFactory(), defaultHandler);
    8 }

    参数解释:

    • corePoolSize:一直保留在线程池中的线程数量,即使它们是空闲的也会赖着不走,除非你设置了allowCoreThreadTimeOut
    • maximumPoolSize:在线程池中允许最大的线程数量
    • keepAliveTime:当线程池中的线程数量比核心多的时候,这个时间是空闲着等待新任务的最长时间,一旦超过这个时间多的线程就会被回收掉
    • unit:keepAliveTime的时间单位
    • workQueue:让还没有分配线程执行的任务进入等待队列,只有当Runnable任务通过execute方法提交之后才会进入队列
    • threadFactory:executor用于创建新线程的工厂
    • handler:当执行受阻之后采取的策略

    corePoolSize

    核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。

    maximumPoolSize

    线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;

    keepAliveTime

    表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

    unit

    参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

    1 TimeUnit.DAYS; //
    2 TimeUnit.HOURS; //小时
    3 TimeUnit.MINUTES; //分钟
    4 TimeUnit.SECONDS; //
    5 TimeUnit.MILLISECONDS; //毫秒
    6 TimeUnit.MICROSECONDS; //微妙
    7 TimeUnit.NANOSECONDS; //纳秒

     workQueue

    一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:

    1 ArrayBlockingQueue
    2 LinkedBlockingQueue
    3 SynchronousQueue

    ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。

    threadFactory

    线程工厂,主要用来创建线程;

    handler

    表示当拒绝处理任务时的策略,有以下四种取值:

    1 ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
    2 ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
    3 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    4 ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

    java中线程池类、接口和方法

    查看相关的源码之后,画出如下的类图:

    路子还是一样的:先是顶层接口,衍生接口,抽象类,实现类,工具类;这些接口或者类中的方法看一下就知道啥意思,但是内部的原理还要看下源码才能明白。

    接下来从以下几方面研究一下线程池:

    1. 线程池状态
    2. 任务的执行
    3. 线程池中的线程初始化
    4. 任务缓存队列及排队策略
    5. 任务拒绝策略
    6. 线程池的关闭
    7. 线程池容量的动态调整

    线程池状态

    使用线程池的时候如何阻塞主线程,使线程池中的线程处理完任务之后,主线程接着执行?

    如何设计一个线程池?

    如何使线程池更加高效?

    有哪些开源的线程池实现?

    如何确定线程池中线程的数量?

    参考

    • https://blog.csdn.net/a369414641/article/details/48342253
    • https://www.cnblogs.com/dolphin0520/p/3932921.html
    • https://www.cnblogs.com/YFYkuner/p/5178684.html
    • https://baike.baidu.com/item/MVCC/6298019
    • https://www.cnblogs.com/chenpingzhao/p/5065316.html
  • 相关阅读:
    Windows Server 2012配置开机启动项
    Windows Server 2019 SSH Server
    NOIP2017 senior A 模拟赛 7.7 T1 棋盘
    Noip 2015 senior 复赛 Day2 子串
    Noip 2015 senior复赛 题解
    Noip 2014 senior Day2 解方程(equation)
    Noip 2014 senior Day2 寻找道路(road)
    Noip 2014 senior Day2 无线网络发射器选址(wireless)
    Noip2014senior复赛 飞扬的小鸟
    Noip 2014 senior 复赛 联合权值(link)
  • 原文地址:https://www.cnblogs.com/tuhooo/p/9527062.html
Copyright © 2011-2022 走看看