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

    简介

    1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

    2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

    java中的对线程池的支持

    默认配置线程池

    newSingleThreadExecutor

    创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

    new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));

    newFixedThreadPool

    创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

    new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());

    任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子.

    newCachedThreadPool

    创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,
    那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

    new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue());

    newScheduledThreadPool

    创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

    ThreadPoolExecutor类

    ThreadPoolExecutor的完整构造方法的签名是:

    ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,RejectedExecutionHandler handler)
    
    • corePoolSize - 池中所保存的线程数,包括空闲线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
    • maximumPoolSize-池中允许的最大线程数。
    • keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
    • unit - keepAliveTime 参数的时间单位。
    • workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute方法提交的 Runnable任务。
    • threadFactory - 执行程序创建新线程时使用的工厂。
    • handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
      ThreadPoolExecutor是Executors类的底层实现。

    任务缓存队列及排队策略

    workQueue的类型为BlockingQueue,通常可以取下面三种类型:

    1)ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;

    2)LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;

    3)synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

    任务拒绝策略

    当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

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

    ThreadPoolExecutor类中有几个非常重要的方法

    execute()

    execute()方法实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。

    submit()

    submit()方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果.

    shutdown():

    当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态。此时,则不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionException异常。但是,此时线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。

    shutdownNow():

    立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

    线程池实现原理

    线程池状态

    在ThreadPoolExecutor中定义了一个volatile变量,另外定义了几个static final变量表示线程池的各个状态:

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

    runState表示当前线程池的状态,它是一个volatile变量用来保证线程之间的可见性;

    下面的几个static final变量表示runState可能的几个取值。

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

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

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

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

    添加流程

    如果运行的线程少于 corePoolSize,则 Executor始终首选添加新的线程,而不进行排队。(如果当前运行的线程小于corePoolSize,则任务根本不会存放,添加到queue中,而是直接抄家伙(thread)开始运行)
    如果运行的线程等于或多于 corePoolSize,则 Executor始终首选将请求加入队列,而不添加新的线程。
    如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

    使用spring task配置线程池

      如果我们仅仅添加<task:annotation-driven/>,也可以使用@Async标签。然而,此时使用的是SimpleAsyncTaskExecutor。如“官方文档27章:Task Execution”中所述,SimpleAsyncTaskExecutor不会使用线程池,仅仅是为每一个请求新开一个线程。这样在大并发的业务场景下,发生OutOfMemory是不足为奇的

    <task:executor id="annotationExecutor" keep-alive="200" pool-size="20-100" queue-capacity="1000" rejection-policy="CALLER_RUNS"/>
    
    <!-- 用阿里可传递ThreadLocal的Executor包装类来提交线程任务 为了支持threadLocal里面的读写数据源传递-->
    <bean name="transmittableExecutor" class="com.alibaba.ttl.threadpool.TtlExecutors" factory-method="getTtlExecutor">
        <constructor-arg ref="annotationExecutor"/>
    </bean>
    
    <!-- 支持 @Async 注解 -->
    <task:annotation-driven executor="transmittableExecutor"/>
    
  • 相关阅读:
    侧滑界面的实现
    Private field 'XXX' is never assigned的解决办法
    android先加载注册页面而不是MainActivity主页面
    每日日报4
    每日日报3
    47 选择排序和插入排序
    计算机启动过程 BIOS MBR等
    ARM中MMU地址转换理解(转)
    深度学习框架 CatBoost 介绍
    预训练词嵌入
  • 原文地址:https://www.cnblogs.com/xckxue/p/8687058.html
Copyright © 2011-2022 走看看