zoukankan      html  css  js  c++  java
  • 线程复用:线程池笔记

    线程复用:线程池

    线程池总概

    什么是线程池?

    接触过JDBC的人,一定听说过数据库连接池(比如,c3p0、Druid等)。其实在我的理解中,两者是差不多的。不过线程池中放的是线程而已。
    线程是一种轻量级工具,但其创建与关闭都需要花费一定的时间。而且大量的线程会抢占内存资源。盲目的大量资源会对系统造成极大的压力。
    线程池,中有一定数量的活跃线程。创建线程变成了从线程池中获得空闲线程;关闭线程变成了向线程池归还线程。

    JDK对于线程池的支持

    Java通过Executors提供五种线程池,分别为:

    • newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    • newFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    • newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。
    • newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
    • newSingleThreadScheduledExcutor创建单线程化的线程池,支持定时及周期性任务执行。

    线程池的使用

    首先是简单使用,这个没有什么特殊之处。
    只需记得newFixedThreadPool创建的是定长的线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    newCachedThreadPool创建的线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程

    定时任务

    newScheduledThreadPool支持定时及周期性任务执行,查看了其源码,主要有以下三种方法:

    • schedule():在给定时间,对任务进行调度;
    • scheduleAtFixedRate() 和 scheduleWithFixedDelay():对任务进行周期性调度,但两者有所区别。
    scheduleAtFixedRate() 和 scheduleWithFixedDelay() 的区别
      1. 两种调度的区别:
        • FixedRate 方式:以上一个任务开始执行时间为起点,在之后的延迟时间后,调用下一次任务。
        • FixedDelay 方式:上一个任务结束后,再经过延迟时间进行任务调度。
      2. 若任务执行时间超过调度时间,
        • FixedRate 方式:若调度时间过短,那么任务会在上一个任务结束后立刻调用(不会出现任务堆叠的现场)。
        • FixedDelay 方式:会严格按照任务间隔时间 = 调度时间 + 任务执行时间
    如果任务遇到异常,那么后续的所有子任务都会停止调度。因此必须保证,异常被及时处理,为周期性任务的稳定调度提供条件。
    

    关于线程池的记录

    拒绝策略

    创建线程池的核心类 ThreadPoolExecutor 有一个参数指定了拒绝策略。拒绝策略,是系统超负荷运行时的补救措施,通常是由于压力太大而引起的,也就是线程池中的线程已经用完了且等待队列已经排满了。
    JDK 提供了四种拒绝策略

    • AbortPolicy 策略:直接抛出异常,阻止系统正常工作。
    • CallerRnsPolicy 策略:只要线程池未关闭,将直接在调用者线程中运行被丢弃的任务。这种做法不会真的丢弃任务,但是任务提交线程的性能将急剧下降
    • DiscardOldestPolicy 策略:丢弃最老的一个请求,也就是即将被执行的任务(处于等待队列的队头),并尝试再次提交当前任务。
    • DiscardPolicy 策略:直接丢掉无法处理的任务。
    • 自定义策略:自己扩展 RejectedExecutionHandler 接口。

    线程扩展

    ThreadPoolExecutor是一个可扩展的线程池,有beforeExecute()afterExecute()terminated()能够对线程进行控制。

    protected void beforeExecute(Thread t, Runnable r) { }
    protected void afterExecute(Runnable r, Throwable t) { }
    protected void terminated() { }
    

    这是三个protected的空方法,摆明了可以让子类扩展。
    * 在执行任务的线程中将调用beforeExecuteafterExecute等方法,在这些方法中还可以添加日志、计时、监视或者统计信息收集的功能。
    * 无论是正常运行,还是抛出异常,都会调用afterExecute。但是,如果抛出Eorror,将不会调用该方法;或者beforeExecute抛出一个RuntimeException,则任务将不被执行,即该方法也不会被调用。
    * 关于terminated,在线程池完成关闭时(就是在所有任务已经完成且所有工作者线程已经关闭),用来释放Executor在生命周期里分配的各种资源,此外还能执行信息通知、日志记录等功能。

    补充

    1. 使用线程池被”吃”掉了异常堆栈信息
    在使用线程池提交线程时,可能会发生异常堆栈信息被”吃”掉的现象,而解决方法:

      1. 放弃submit(),改用execute()。    
      2. 获取submit()方法返回类的get()方法。      

      • Future future = pools.submit(new Thread());    
        future.get();
        

           3. 扩展 ThreadPoolExecutor 线程池,让其在调度任务前,先保存提交任务线程的堆栈消息(就是重写线程池线程的调用方法)。 

       

    2.自定义线程:ThreadFactory
    这个接口只有一个方法 newThread(Runnable r),主要是由线程池调用新建线程。

    3. 优化线程池线程数量     
    在《Java Concurrency in Practice》书中给了一个估算线程池大小的经验公式(同时,在Java中,可以通过Runtime.getRuntime().availableProcessors获取可用的CPU数量。)。

    Ncpu = CPU数量
    Ucpu = 目标CPU的使用率,0 <= Ucpu <= 1
    W/C = 等待时间与计算时间的比率
    所以,最优的线程池大小为:
    Nthreads = Ncpu * Ucpu * ( 1 + W/C )
    
  • 相关阅读:
    形象理解ERP(转)
    禁用windows server 2008 域密码复杂性要求策略
    How to adding find,filter,remove filter on display method Form
    Windows Server 2008 R2激活工具
    How to using bat command running VS development SSRS report
    Creating Your First Mac AppGetting Started
    Creating Your First Mac AppAdding a Track Object 添加一个 Track 对象
    Creating Your First Mac AppImplementing Action Methods 实现动作方法
    Creating Your First Mac AppReviewing the Code 审查代码
    Creating Your First Mac AppConfiguring the window 设置窗口
  • 原文地址:https://www.cnblogs.com/MaxElephant/p/8109549.html
Copyright © 2011-2022 走看看