ThreadPoolExecutor

概述:

在编程规范中,不建议使用Executors去创建线程池,而是推荐使用ThreadPoolExecutor。

ThreadPoolExecutor会更明确运行规则,避免资源耗尽的风险。

因为Executors返回线程池有弊端:

1)FixedThreadPool和SingleThreadPool,允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。

2)CachedThreadPool和ScheduledThreadPool,允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

ThreadPoolExecutor:

public ThreadPoolExecutor (
            int corePoolsize,
            int maximumPoolSize,
            1ong keepAliveTime,
            TimeUnit unit,
            BlockingQueue<Runnable> workQueue,
            ThreadFactory  threadFactory,
            RejectedExecutionHandler handler)
函数的参数含义如下:.
    corePoolSize:指定了线程池中的线程数量。
    maximumPoolSize:指定了线程池的最大线程数量。
    keepAliveTime当线程池线程数量超过corePoolSize时
    ,多余的空闲线程的存活时间。即,超过corePoolSize的空闲线程,在多长时间内会被销毁。
    unit: keepAliveTime的单位。
    workQueue:任务队列,被提交但尚未被执行的任务
    threadFactory:线程工厂,用于创建线程,一般用默认的即可
    handler:拒绝策略。当任务太多来不及处理,如何拒绝任务。以上参数中,大部分都很简单
    ,只有workQueue和handler需要进行详细说明

在这里插入图片描述

任务队列:

参数workQueue指被提交但未执行的任务队列,它是一个BlockingQueue接口的对象,:仅用于在放Runnable对象。根据队列功能分类,Thread
PoolExecutor的构造函数中可使,,用以下几种BlockingQueue:"
     直接提交的对列:该功能由SynchronousQueue对象提供。SynchronousQueue是个特殊的BlockingQueue.,SynchronousQueue没有容
        量,每一个插入操作都要等待.一个相应的删除操作,反之,每一个删除操作都要等待对应的插入操作。
	    SynchronousQueue不保存任务,它总是将任务提交给线程执行,如果没有空闲的,进程,则尝试创建新的进程,如果进程数量已经达到最天值,则执行拒绝策略。
	    因此,使用SynchronousQueue队列,通常要设置很大的-maximumPoolSize值,否则很容易执行异常策略
     有界的任务队列:有界的任务队列可以使用ArrayBlockingQueue实现:
        ArrayBlockingQueue的构造函数必须带一个容量参数,表示该队列的最大容量:public ArrayBlockingQueue (int capacity)
        ,当使用有界的任务队列时,若有新的任务需要执行,如果线程池的实际线程数小于.corePoolSize,则会优先创建新的线程,若大于corePoolSize,则会将新任务加入等待对列。若等待队
        列已满,无法加入,则在总线程数不大于maximumPoolSize的前
        提下,创建新的进程执行任务。若大于maximumPoolSize,则执行拒绝策略。可见,有界队列仅当任务队列装满时,才可能将
        线程数提升到corePoolSize以上,换言之,除非系统非常繁忙,否则确保核心线程数维持在corePoolSize.
     无界的任务队列:
        无界任务队列可以通过LinkedBlockingQueue类实现。与有界队列相比,除非系统资源耗尽,否则无界的任务队列
        不存在任务入队失败的情况。当有新的住务到来,系统的线程数
        小于corePoolSize时,线程池会生成新的线程执行任务,但当系统的线程数达到corePoolSize后,就不会继续增加。若后续仍有:的
        任务加入,而又没有空闲的线程资源,则任务直接进入队列等待。若任务创建..和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。
     优先任务队列:
        优先任务队列是带有执行优先级的队列,它通过PriorityBlockingQueue实现。可以控制任务的执行先后顺序,是一个特殊的无
        界队列。无论是有界队列ArrayBlockingQueue,还是未指定大小的无界队列LinkedBlockingQueue都是按照先进先出算法处理任
        的。而PriorityBlockingQueue则可以根据任务自身的优先级顺序先后执行,在确保系统性能的同时,也能有很好的质量保证
        (总是确保高优先级的任务先执行)

拒绝策略:

JDK内置的拒维策略如下:
AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。
CallerRunsPolicy策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。
DiscardOledestPolicy策略:该策略将丢弃最老的一个请求,也就是即将被执行的幽一个任务,并尝试再次提交当前任务。
DiscardPolicy策略:该策略默默地丢弃无法处理的任务,不予任何处理。
以上内置的策略均实现了RejectedExecutionHandler接口,若以 策略仍无法满足实际,应用需要,完全可以自己扩展RejectedExecutionHandler RejectedExecutionHandler的定义

阿里规约之所以强制要求手动创建线程池,也是和这些参数有关。具体为什么不允许,规约是这么说的:

线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

Executor提供的四个静态方法创建线程池,但是阿里规约却并不建议使用它。

Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
  主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
  主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

看一下这两种弊端怎么导致的。

第一种,newFixedThreadPool和newSingleThreadExecutor分别获得 FixedThreadPool 类型的线程池 和 SingleThreadExecutor 类型的线程池。

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
  }

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
 }

因为,创建了一个无界队列LinkedBlockingQueuesize,是一个最大值为Integer.MAX_VALUE的线程阻塞队列,当添加任务的速度大于线程池处理任务的速度,可能会在队列堆积大量的请求,消耗很大的内存,甚至导致OOM。

第二种,newCachedThreadPool 和 newScheduledThreadPool创建的分别是CachedThreadPool 类型和 ScheduledThreadPoolExecutorScheduledThreadPoolExecutor类型的线程池。

CachedThreadPool是一个会根据需要创建新线程的线程池 ,ScheduledThreadPoolExecutor可以用来在给定延时后执行异步任务或者周期性执行任务。

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
}

创建的线程池允许的最大线程数是Integer.MAX_VALUE,空闲线程存活时间为0,当添加任务的速度大于线程池处理任务的速度,可能会创建大量的线程,消耗资源,甚至导致OOM。

这两种都是有点极端的,稍微点进去看一下源码就能看出来。

阿里规约提倡手动创建线程池,而非Java内置的线程池,给出的正例如下:

正例1:

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

正例2:

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:

<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(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) 用给定的初始参数和默认的线程工厂及被拒绝的执行处理程序创建新的 | |
|ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler)用给定的初始参数和默认的线程工厂创建新的 ThreadPoolExecutor。|
|ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory)用给定的初始参数和默认被拒绝的执行处理程序创建新的 ThreadPoolExecutor。|
|ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) 用给定的初始参数创建新的 ThreadPoolExecutor。|

示例:

自定义ThreadFactory:


public class Task implements Runnable{

    private int i;

    public Task(int i){this.i = i;}

 

    @Override

    public void run() {

        try {

            Thread.sleep(10000L);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("ThreadName:"+Thread.currentThread().getName()+"线程执行:"+i);

    }

}

简单使用:


private static void simple(){

        ThreadFactory namedThreadFactory = new MyThreadFactory();

        int queueCapacity = 3, corePoolSize=2, maximumPoolSize=3;

        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(queueCapacity);

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,10, TimeUnit.SECONDS,arrayBlockingQueue,namedThreadFactory);

        for(int i=1;i<5;i++){

                Thread thread = namedThreadFactory.newThread(new Task(i));

                threadPoolExecutor.execute(thread);

                System.out.println("i:"+i+", queueSize:"+arrayBlockingQueue.size()

                        +", poolSize:"+threadPoolExecutor.getPoolSize()

                        +", coreSize:"+threadPoolExecutor.getCorePoolSize()

                        +", maxSize:"+threadPoolExecutor.getMaximumPoolSize());

 

        }

        threadPoolExecutor.shutdown();

        while (true){

            if(threadPoolExecutor.isTerminated()){

                System.out.println("over");

                break;

            }

        }

        System.out.println( );

    }

使用LinkedBlockingQueue作为示例队列:


private static void LinkedBlockingQueue(){

        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()

                .setNameFormat("demo-pool-%d").build();

        int queueCapacity = 3, corePoolSize=2, maximumPoolSize=3;

        LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue(queueCapacity);

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,10, TimeUnit.SECONDS,linkedBlockingQueue,namedThreadFactory);

        for(int i=1;i<21;i++){

            Thread thread = namedThreadFactory.newThread(new Task(i));

            System.out.println("即将添加数据:"+i);

            threadPoolExecutor.execute(thread);

            System.out.println("i:"+i+", queueSize:"+linkedBlockingQueue.size()

                    +", poolSize:"+threadPoolExecutor.getPoolSize()

                    +", coreSize:"+threadPoolExecutor.getCorePoolSize()

                    +", maxSize:"+threadPoolExecutor.getMaximumPoolSize());

        }

查看打印的日志:


即将添加数据:1

i:1, queueSize:0, poolSize:1, coreSize:2, maxSize:3

即将添加数据:2

i:2, queueSize:0, poolSize:2, coreSize:2, maxSize:3

即将添加数据:3

i:3, queueSize:1, poolSize:2, coreSize:2, maxSize:3

即将添加数据:4

i:4, queueSize:2, poolSize:2, coreSize:2, maxSize:3

即将添加数据:5

i:5, queueSize:3, poolSize:2, coreSize:2, maxSize:3

即将添加数据:6

i:6, queueSize:3, poolSize:3, coreSize:2, maxSize:3

即将添加数据:7

Exception in thread "main" java.util.concurrent.RejectedExecutionException

当添加第一个任务的时候,由于线程池空着,直接创建核心线程来处理请求;

当已经添加完两个请求,添加第三个请求的时候,核心线程数已满,则往队列里面添加,此时queueSize=1;

添加3、4、5任务,都被添加到了队列里面,此时queueSize=3;

添加6任务的时候,核心线程已满,队列已满,运行的线程数小于maximumPoolSize,那么线程池再处理一个任务,此时poolSize=3,线程池的任务满了;

添加7任务的时候,由于线程池里面的任务还没有执行完,而队列也是满的,线程池处理不了这么多任务了,抛出异常java.util.concurrent.RejectedExecutionException。

综上:先创建核心线程,够数后往队列里面塞,塞满继续创建执行线程,再满后抛出拒绝执行的异常。我们在执行的时候希望往池子里面一直扔,盛不下了也别抛异常,怎么办?

可以通过判断池子的大小,如果已经满了,则阻塞添加:


private static void LinkedBlockingQueue(){

        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()

                .setNameFormat("demo-pool-%d").build();

        int queueCapacity = 3, corePoolSize=2, maximumPoolSize=3;

        LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue(queueCapacity);

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,10, TimeUnit.SECONDS,linkedBlockingQueue,namedThreadFactory);

        for(int i=1;i<21;i++){

            Thread thread = namedThreadFactory.newThread(new Task(i));

            System.out.println("即将添加数据:"+i);

            threadPoolExecutor.execute(thread);

            System.out.println("i:"+i+", queueSize:"+linkedBlockingQueue.size()

                    +", poolSize:"+threadPoolExecutor.getPoolSize()

                    +", coreSize:"+threadPoolExecutor.getCorePoolSize()

                    +", maxSize:"+threadPoolExecutor.getMaximumPoolSize());

            while((threadPoolExecutor.getPoolSize()+linkedBlockingQueue.size())==(queueCapacity + maximumPoolSize)){

                System.out.println("线程池已满,休眠等待 i:"+i+", queueSize:"+linkedBlockingQueue.size()

                        +", poolSize:"+threadPoolExecutor.getPoolSize()

                        +", coreSize:"+threadPoolExecutor.getCorePoolSize()

                        +", maxSize:"+threadPoolExecutor.getMaximumPoolSize());

                try {

                    Thread.sleep(1000L);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        }

 

        threadPoolExecutor.shutdown();

        while (true){

            if(threadPoolExecutor.isTerminated()){

                System.out.println("run over");

                break;

            }

        }

    }

在for循环中进行了判断:

(threadPoolExecutor.getPoolSize()+linkedBlockingQueue.size())==(queueCapacity + maximumPoolSize),表示当前队列里面的线程数加上线程池里面当前线程数等于当前线程池可处理的最大线程的时候,进行Thread.sleep(1000L)等待,知道线程池有空闲资源的时候继续执行添加操作。

避免异常,可以使用阻塞队列ArrayBlockingQueue:


private static void ArrayBlockingQueue(){

        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()

                .setNameFormat("demo-pool-%d").build();

        int queueCapacity = 3, corePoolSize=2, maximumPoolSize=3;

        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(queueCapacity);

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,10, TimeUnit.SECONDS,arrayBlockingQueue,namedThreadFactory);

        for(int i=1;i<21;i++){

            int finalI = i;

            if((threadPoolExecutor.getPoolSize()+arrayBlockingQueue.size())>=(queueCapacity+maximumPoolSize)){

                try {

                    Thread thread = namedThreadFactory.newThread(new Task(finalI));

                    arrayBlockingQueue.put(thread);

                    System.out.println("队列中添加线程 i:"+i+", queueSize:"+arrayBlockingQueue.size()

                            +", poolSize:"+threadPoolExecutor.getPoolSize()

                            +", coreSize:"+threadPoolExecutor.getCorePoolSize()

                            +", maxSize:"+threadPoolExecutor.getMaximumPoolSize());

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }else {

                Thread thread = namedThreadFactory.newThread(new Task(finalI));

                threadPoolExecutor.execute(thread);

                System.out.println("i:"+i+", queueSize:"+arrayBlockingQueue.size()

                        +", poolSize:"+threadPoolExecutor.getPoolSize()

                        +", coreSize:"+threadPoolExecutor.getCorePoolSize()

                        +", maxSize:"+threadPoolExecutor.getMaximumPoolSize());

            }

        }

        threadPoolExecutor.shutdown();

        while (true){

            if(threadPoolExecutor.isTerminated()){

                System.out.println("over");

                break;

            }

        }

        System.out.println( );

    }

使用ArrayBlockingQueue 阻塞机制,来实现同Thread.sleep()同样的效果。

简单使用:

public class ThreadTest01 {
    /**
     * 异步执行器
     */
    public final ThreadPoolExecutor executor =new ThreadPoolExecutor(1,
            10,
            60,
            TimeUnit.SECONDS,
            new SynchronousQueue<Runnable>(),
            new ThreadPoolExecutor.AbortPolicy());


    void exec(String eventCode){
        executor.execute(new MessageThreadTask(eventCode));
    }

    public static void main(String[] args) {
        new ThreadTest01().exec("THIS_NOT_ONLY_TEST");
    }

    private class MessageThreadTask implements Runnable{

        private String eventCode;

        public MessageThreadTask(String eventCode) {
            this.eventCode = eventCode;
        }

        @Override
        public void run() {
            if ("THIS_NOT_ONLY_TEST".equals(eventCode)){
                System.out.println("this not only a test");
            }
        }
    }
}

在线程池中获取线程执行的返回值参考:
https://blog.csdn.net/weixin_44190113/article/details/106906419

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值