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

    使用线程池的好处:

    1. 降低资源消耗。重复利用已创建线程,降低线程创建与销毁的资源消耗。(线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务。)
    2. 提高响应效率。任务到达时,不需等待创建线程就能立即执行。
    3. 提高线程可管理性。统一分配、调优和监控。
    4. 防止服务器过载。内存溢出、CPU耗尽。

    线程池的基本组成部分:

    1. 线程池管理器(ThreadPool):用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务;
    2. 工作线程(PoolWorker):我们把用来执行用户任务的线程称为工作线程,工作线程就是不断从队列中获取任务对象并执行对象上的业务方法。 
    3. 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
    4. 任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

    Java中线程池有关接口和类:

    创建线程池:

    public ThreadPoolExecutor(int corePoolSize,  //核心线程数,即使在无用时也不会被回收
                                  int maximumPoolSize, //可容纳的最大线程数
                                  long keepAliveTime,  //非核心线程可保留的最长空闲时间
                                  TimeUnit unit,  //keepAliveTime的单位,如分钟(MINUTES)
                                  BlockingQueue<Runnable> workQueue,  //任务等待队列
                                  ThreadFactory threadFactory,  //创建线程的线程工厂
                                  RejectedExecutionHandler handler  //任务满时的拒绝策略 ) 

         在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。     

    Executors.newFixedThreadPool(int);    //创建固定容量大小的缓冲池
    Executors.newSingleThreadExecutor();   //创建容量为1的缓冲池
    Executors.newCachedThreadPool();        //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
    1. newFixedThreadPool:创建的线程池corePoolSize和maximumPoolSize值是相等的(只有核心线程),它使用的LinkedBlockingQueue;
    2. newSingleThreadExecutor:将corePoolSize和maximumPoolSize都设置为1(只有一条核心线程来执行任务),也使用的LinkedBlockingQueue。适用于有顺序的任务的应用场景。
    3. newCachedThreadPool:将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。适用于耗时少,任务量大的情况。
    4.  newScheduledThreadPool :创建一个定长线程池,支持定时及周期性任务执行。

          实际中,如果Executors提供的三个静态方法能满足要求,就尽量使用它提供的三个方法,因为自己去手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数量来进行配置。

    向线程池提交新任务流程图:

    1. 如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。
    2. 如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。
    3. 如果无法将任务加入BlockingQueue(队列已满),则在非corePool中创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。
    4. 如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。

    任务缓存队列:

           workQueue,它用来存放等待执行的任务,它的类型为BlockingQueue<Runnable>,通常可以取下面三种类型:

    1. ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
    2. LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
    3. synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务,如果有空闲线程则会重复使用,线程空闲了60秒后会被回收。

    handler的拒绝策略有四种:

    • ThreadPoolExecutor.AbortPolicy:丢弃新任务,直接抛出异常,提示线程池已满(默认)              
    • ThreadPoolExecutor.DisCardPolicy:丢弃新任务,也不抛出异常              
    • ThreadPoolExecutor.DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行              
    • ThreadPoolExecutor.CallerRunsPolicy:直接在当前线程中调用execute来执行当前任务

          此外,用户自定义拒绝策略:实现RejectedExecutionHandler,并自己定义策略模式 

    向线程池提交新任务:

          execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功。

    threadsPool.execute(new Runnable() {
            @Override
            public void run() {
            }
        });

    关闭线程池:

      ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中:

    • shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
    • shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

    动态调整容量:

      ThreadPoolExecutor提供了动态调整线程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),

    • setCorePoolSize:设置核心池大小
    • setMaximumPoolSize:设置线程池最大能创建的线程数目大小

    合理配置线程池大小:

      一般需要根据任务的类型来配置线程池大小:

      如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 NCPU+1

      如果是IO密集型任务,参考值可以设置为2*NCPU

    线程池的状态

    volatile int runState;
    static final int RUNNING = 0;
    static final int SHUTDOWN = 1;
    static final int STOP = 2;
    static final int TERMINATED = 3;

    当创建线程池后,初始时,线程池处于RUNNING状态;

      如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;

      如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;

          当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。

     
  • 相关阅读:
    【leetcode】1630. Arithmetic Subarrays
    【leetcode】1629. Slowest Key
    【leetcode】1624. Largest Substring Between Two Equal Characters
    【leetcode】1620. Coordinate With Maximum Network Quality
    【leetcode】1619. Mean of Array After Removing Some Elements
    【leetcode】1609. Even Odd Tree
    【leetcode】1608. Special Array With X Elements Greater Than or Equal X
    【leetcode】1603. Design Parking System
    【leetcode】1598. Crawler Log Folder
    Java基础加强总结(三)——代理(Proxy)Java实现Ip代理池
  • 原文地址:https://www.cnblogs.com/kikis/p/9770793.html
Copyright © 2011-2022 走看看