介绍:
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。
为什么要使用线程池?
这里借用《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