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
  • 相关阅读:
    什么是 Spring 框架?Spring框架有哪些主要模块?
    java动态代理实现与原理详细分析
    react中 函数式组件hook的使用
    JavaScript中的delete的定义以及使用
    React中ref的使用
    使用react中antd design UI库from中的getFieldsValue,getFieldValue,validateFields,resetFields,getFieldDecorator,initialvalue的用法
    使用react和axios实现的城市三级联动
    关于react中antd design pro下面src/models
    关于thisState的那些事
    react的生命周期
  • 原文地址:https://www.cnblogs.com/tuhooo/p/9527062.html
Copyright © 2011-2022 走看看