• 并发编程-线程池(五)线程池为什么不允许使用Executors创建


      在实际使用线程池的时候,手动创建使用 Excecutor 创建线程会有以下问题。【1】

    1、不受控风险大;

      无法知道实际生产中,创建和使用的线程数量;

      如果每个请求都创建一个线程池,并且通过线程池创建的线程,这样无法控制具体的线程数量;

    2、开销大;

      如果每个请求都创建一个线程池,并且通过线程池创建的线程,内存就很容易使用完;

      同时,会造成线程切换的上下文切换的开销;

      创建一个线程的过程

        上面已经提到了,创建一个线程还要调用操作系统内核API。为了更好的理解创建并启动一个线程的开销,我们需要看看 JVM 在背后帮我们做了哪些事情:

        1)它为一个线程栈分配内存,该栈为每个线程方法调用保存一个栈帧
        2)每一栈帧由一个局部变量数组、返回值、操作数堆栈和常量池组成
        3)一些支持本机方法的 jvm 也会分配一个本机堆栈
        4)每个线程获得一个程序计数器,告诉它当前处理器执行的指令是什么
        5)系统创建一个与Java线程对应的本机线程
        6)将与线程相关的描述符添加到JVM内部数据结构中
        7)线程共享堆和方法区域

        

    一、Executors 线程池创建工具【2】

    1、Executors.newCachedThreadPool();
    说明:

       创建的线程池核心线程0 , 最大线程是Integer.MaxValue。 线程空闲存活时间1分钟。 默认异常拒绝策略,使用SynchronousQueue队

    特点:

      每次添加任务如果没有空闲线程就会新建一个线程去执行。
      SynchronousQueue是阻塞队列,加入任务的线程会阻塞住,直到其它线程从中取走任务才会结束阻塞
      线程创建上限近乎无限

    适用场景:

      所以它适用于任务加入比较稳当且加入间隔短的场景

    实现:

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

    缺点:

      任务队列是SynchronousQueue,线程池对任务来着不拒,线程不够用就创建一个线程。

      如果同一时刻应用的来了大量的任务, 这个线程池很容易就创建过多的线程, 容易导致应用卡顿或者直接OOM

    2、Executors.newFixedThreadPool(int);
    说明: 

      核心线程和最大线程数是你传入的参数。 其他参数和 Executors.newSingleThreadExecutor一样

    实现:

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

    缺点:

      这个定死了线程数量, 所以线程数量是不会超出的,但是它的任务队列是无界的LinkedBlockingQueue

      加进来的任务处理不过来就会存入任务队列中, 并且无限制的存入队列,很容易导致OOM。

    3、Executors.newSingleThreadExecutor();
    说明:

      创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照顺序执行。

    特点:

      只有一个线程

      近乎可以接收无限任务的队列, 可以堆积大量任务

      适用于任务持续加入但是任务数并不多的场景

    实现:

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

    缺点:

      任务队列和上面一样, 没有限制, 很容易就使用不当导致OOM

    4、Executors.newScheduledThreadPool(int);
    说明:

      创建一个定长线程池,支持定时及周期性任务执行。

    特点:

      核心线程是传入的参数,最大线程是int上线, 默认存活时间是10毫秒, 任务队列使用自己实现的DelayedWorkQueue, 拒绝策略异常策略

      加入任务的时候,会把任务和定时时间构建一个RunnableScheduledFuture对象,再把这个对象放入DelayedWorkQueue队列中,

      DelayedWorkQueue是一个有序队列, 他会根据内部的RunnableScheduledFuture的运行时间排序内部对象。

      任务加入后就会启动一个线程。 这个线程会从DelayedWorkQueue中获取一个任务。

      DelayedWorkQueue内部是按照时间从前完后获取任务的。如果任务的中的时间还没有到。 获取的就是null。 获取任务结束,线程会休眠10毫秒。所以这个定时任务的执行最小间隔是10毫秒的。

    内部实现

      new ScheduledThreadPoolExecutor(corePoolSize)

    缺点:

      这个是定时任务的线程池, 没有定义线程创建数量的上线, 同时任务队列也没有定义上限, 如果前一次定时任务还没有完成, 后一个定时任务的运行时间到了, 它也会运行, 线程不够就创建。

      这样如果定时任务运行的时间过长, 就会导致前后两个定时任务同时执行,如果他们之间有锁,还有可能出现死锁。

    二、线程池推荐创建方式【3】

    1、 方式一:引入 commons-lang3包

     ScheduledExecutorService executorService =
      new ScheduledThreadPoolExecutor(1,new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());

      

    2、方式二:引入:com.google.guava包  

        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
            .setNameFormat("demo-pool-%d").build();
     
        //Common Thread Pool
        ExecutorService pool = new ThreadPoolExecutor(5, 200,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
     
        pool.execute(()-> System.out.println(Thread.currentThread().getName()));
        pool.shutdown();//gracefully shutdown
    

      

    3、方式三:spring配置线程池方式:自定义线程工厂bean需要实现ThreadFactory,可参考该接口的其它默认实现类,使用方式直接注入bean

          调用execute(Runnable task)方法即可

    <bean id="userThreadPool"
            class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
            <property name="corePoolSize" value="10" />
            <property name="maxPoolSize" value="100" />
            <property name="queueCapacity" value="2000" />
     
        <property name="threadFactory" value= threadFactory />
            <property name="rejectedExecutionHandler">
                <ref local="rejectedExecutionHandler" />
            </property>
        </bean>
        //in code
        userThreadPool.execute(thread);
    

      

    三、ThreadPoolExecutor 使用【4】

      https://www.cnblogs.com/dafanjoy/p/9729358.html

    ---------------------------------------------------------------------------------------------------------------------

    来源:

    【1】https://zhuanlan.zhihu.com/p/134204957

    【2】https://blog.csdn.net/leisurelen/article/details/107872827?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control

    【3】

    【4】https://www.cnblogs.com/dafanjoy/p/9729358.html

    【5】https://www.cnblogs.com/dukedu/p/13813443.html

  • 相关阅读:
    codechef BIBOARD
    Tree
    NOIWC2021总结
    星际穿越
    GDKOI总结
    lg4229
    P3320 [SDOI2015]寻宝游戏
    P6670 [清华集训2016] 汽水
    P6326 Shopping
    P3060 [USACO12NOV]Balanced Trees G
  • 原文地址:https://www.cnblogs.com/Jomini/p/14052749.html
Copyright © 2020-2023  润新知