executors与threadPoolExecutor区别

介绍:
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。
为什么要使用线程池?
这里借用《Java并发编程的艺术》提到的来说一下使用线程池的好处:

降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度。 当任务到达时,任务可以不需要的等到线程创建就能立即执行。
提高线程的可管理性。 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

如何创建线程池?
《阿里巴巴Java开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 new ThreadPoolExecutor 实例的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

使用Executors哪里有风险?有什么风险?
Executors 返回线程池对象的弊端如下:

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

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

下面就分析

分析源码:
先看Executors类,这里先看Executors类调用了哪些方法,至于方法参数的意义,先不用管,后面有详细介绍:
public class Executors {

/**
 * 创建固定数量线程的线程池
 *
 * 这是最后一个参数--阻塞队列调用的方法,长度是Integer.MAX_VALUE
 * public LinkedBlockingQueue() {
 *    this(Integer.MAX_VALUE);
 * }
 *
 * @param nThreads the number of threads in the pool
 * @return the newly created thread pool
 * @throws IllegalArgumentException if {@code nThreads <= 0}
 */
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}


/**
 * 创建只有一个线程的线程池
 *
 * 这是最后一个参数--阻塞队列调用的方法,长度也是Integer.MAX_VALUE
 * public LinkedBlockingQueue() {
 *    this(Integer.MAX_VALUE);
 * }
 *
 * @return the newly created single-threaded Executor
 */
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}


/**
 *创建一个缓冲线程池 
 *
 * 这是最后一个参数--阻塞队列调用的方法
 * public SynchronousQueue() {
 *    this(false);
 * }
 *
 * 它的第二个参数,maximumPoolSize 为Integer.MAX_VALUE
 *
 * @return the newly created thread pool
 */
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}


/**
 * Creates a thread pool that can schedule commands to run after a
 * given delay, or to execute periodically.
 *
 * 创建一个可以在给定延迟后再执行或定期执行命令的线程池
 *
 * ScheduledThreadPoolExecutor 是 ThreadPoolExecutor 的子类,代码如下:
 *
 public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor
    implements ScheduledExecutorService {
        
        //这是下面调用的构造方法,其实是调用了父类的构造方法,这些参数都是下面分析的参数
        public ScheduledThreadPoolExecutor(int corePoolSize) {
           super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
           new DelayedWorkQueue());
        }
        
 }

 *
 * @param corePoolSize the number of threads to keep in the pool,
 * even if they are idle
 * @return a newly created scheduled thread pool
 * @throws IllegalArgumentException if {@code corePoolSize < 0}
 */
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {

    return new ScheduledThreadPoolExecutor(corePoolSize);
}

}

可以看到Executors类创建线程池的时候实际就是调用ThreadPoolExecutor类的构造方法来创建。

再看ThreadPoolExecutor类:

public class ThreadPoolExecutor extends AbstractExecutorService {

/**
* 其中一个构造方法:newFixedThreadPool时调用的
*
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default thread factory and rejected execution handler.
* It may be more convenient to use one of the {@link Executors} factory
* methods instead of this general purpose constructor.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @throws IllegalArgumentException if one of the following holds:

* {@code corePoolSize < 0}

* {@code keepAliveTime < 0}

* {@code maximumPoolSize <= 0}

* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}

}

参数介绍:

corePoolSize

在线程池中保持的线程的数量,即使是这些线程没有被使用,除非设置了线程超时时间

maximumPoolSize

最大线程数量,当workQueue队列已满,放不下新的任务,再通过execute添加新的任务则线程池会再创建新的线程,线程数量大于corePoolSize但不会超过maximumPoolSize,如果超过maximumPoolSize,那么会抛出异常,如RejectedExecutionException。

keepAliveTime和unit

当线程池中线程数量大于workQueue,如果一个线程的空闲时间大于keepAliveTime,则该线程会被销毁。unit则是keepAliveTime的时间单位。

workQueue

阻塞队列,当线程池正在运行的线程数量已经达到corePoolSize,那么再通过execute添加新的任务则会被加到workQueue队列中,在队列中排队等待执行,而不会立即执行。

为什么说
FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。
CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。
再结合注释看Executors类中的传参就明白了!
Executors中创建线程池:

FixedThreadPool 和 SingleThreadExecutor 传入的最后一个参数阻塞队列 ”workQueue“,默认的长度是INTEGER.MAX_VALUE,而它们允许的最大线程数量又是有限的,所以当请求线程的任务过多线程不够用时,它们会在队列中等待,又因为队列的长度特别长,所以可能会堆积大量的请求,导致OOM。

CachedThreadPool 和 ScheduledThreadPool 它们的阻塞队列长度有限,但是传入的第二个参数maximumPoolSize 为Integer.MAX_VALUE,这就意味着当请求线程的任务过多线程不够而且队列也满了的时候,线程池就会创建新的线程,因为它允许的最大线程数量是相当大的,所以可能会创建大量线程,导致OOM。

下面我们来写个例子看看效果。

public class MyThreadTest implements Runnable,Comparable{

private static ExecutorService executorService=Executors.newFixedThreadPool(100);

private static Executor myThread=new ThreadPoolExecutor(100,100,0L, TimeUnit.SECONDS,new PriorityBlockingQueue<Runnable>());
private String name;
public MyThreadTest(String name){
    this.name=name;
}
public MyThreadTest(){
    name="";
}

@Override
public void run(){
    try{
        Thread.sleep(100);
        System.out.println(name+" running....");
    }catch(InterruptedException e){
        e.printStackTrace();
    }
}
@Override
public int compareTo(MyThreadTest o){
    int me = Integer.valueOf(name.split("_")[1]);
    int other = Integer.valueOf(o.name.split("_")[1]);
    return me-other;
}


public static void main(String[] args) {
    for (int i=0;i<1000;i++){

// myThread.execute(new MyThreadTest(“test_”+(i)));

        executorService.execute(new MyThreadTest("test_"+(i)));

    }
}

}

test_56 running…
test_39 running…
test_37 running…
test_43 running…
test_57 running…
test_42 running…
test_33 running…
test_15 running…
test_97 running…
test_1 running…
test_74 running…
test_54 running…
。。。
。。。
。。。
test_948 running…
test_943 running…
test_942 running…
test_940 running…
test_941 running…
test_929 running…
test_934 running…
test_932 running…
test_926 running…
test_925 running…
test_923 running…
两者执行效率差不多,如果没有特殊要求建议使用

ExecutorService executorService=Executors.newFixedThreadPool(100);这种方式很简单快捷。如果需要进行其他特殊需求可换成
Executor myThread=new ThreadPoolExecutor(100,100,0L, TimeUnit.SECONDS,new PriorityBlockingQueue());

其中new PriorityBlockingQueue()可以自己定义queue

总结:
Executors类中封装好的创建线程池的方法使用方便,但是也有其局限性和风险性,所以我们可以使用 ThreadPoolExecutor 类中的构造方法手动创建线程池的实例, 从而可以根据我们的使用情况来指定参数,满足使用的同时又能规避风险。

所以,说白了,使用Executors类创建线程池与使用ThreadPoolExecutor类的区别就是使用ThreadPoolExecutor类可以自定义传入我们设置的线程池的参数,更加灵活。

原文:https://blog.csdn.net/SmallCarrot/article/details/85013098
原文:https://blog.csdn.net/qq_36565494/article/details/82802989

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值