并发编程-线程池(五)线程池为什么不允许使用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

 

posted @ 2020-11-28 15:26  抽象Java  阅读(501)  评论(0编辑  收藏  举报