标签:说明 支持 keep src 长度 工厂方法 默认值 大于 stat
线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。
在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。
一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。
Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处:
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用线程池,必须对其实现原理了如指掌。
JDK提供了Executor框架,可以让我们有效的管理和控制我们的线程,其实质也就是一个线程池。Executor下的接口和类继承关系如下:
其中,Executors相当于是一个工具类,提供了一系列静态工厂方法用于创建各种线程池:
挑几个比较重要的来看一下:
newFixedThreadPool:该方法返回一个固定线程数量的线程池;
newSingleThreadExecutor:该方法返回一个只有一个现成的线程池;
newCachedThreadPool:返回一个可以根据实际情况调整线程数量的线程池;
newSingleThreadScheduledExecutor:该方法和newSingleThreadExecutor的区别是给定了时间执行某任务的功能,可以进行定时执行等;
newScheduledThreadPool:在4的基础上可以指定线程数量。
创建线程池实质调用的还是ThreadPoolExecutor
在Executors类中,我们拿出来一个方法简单分析一下:
可以看出,类似的其他方法一样,在Executors内部创建线程池的时候,实际创建的都是一个ThreadPoolExecutor对象,只是对ThreadPoolExecutor构造方法,进行了默认值的设定。ThreadPoolExecutor的构造方法如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
....
}
方法的内容略看,仔细分析一下它的参数:
尝试使用一下线程池:
public static void threadPoolTest() {
//ExecutorService threadPool = Executors.newFixedThreadPool(5);
//ExecutorService threadPool = Executors.newSingleThreadExecutor();
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 1;i<=10;i++){
threadPool.execute(()->{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t办理业务");
});
}
threadPool.shutdown();
}
参考阿里巴巴java开发手册:
【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。 说明:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。说明:Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
手写一个线程池:(不考虑拒绝策略)
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executor = new ThreadPoolExecutor(
2,
6,
2,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(3),
Executors.defaultThreadFactory()
);
for (int i = 1;i<5;i++){
executor.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t办理业务");
});
}
executor.shutdown();
}}
其实线程提交可以使用execute和submit这两种方法,但是两者有所不同:
(1)execute()
方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功。通过以下代码可知execute()
方法输入的任务是一个Runnable类的实例。
(2)submit()
方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()
方法来获取返回值,get()
方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)
方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。
先从一张看起,详细描述了一个任务的执行情况和线程池的工作过程:
我们来解释一下整个流程:
1)在创建了线程池之后等待提交任务请求过来;
2)当调用了execute()方法提交一个任务请求的时候,线程池会执行以下的判断:
3)当一个线程完成任务的时候,它会从队列中取出下一个任务来执行;
4)当一个线程无事可做超过keepAliveTime时,线程会停掉大于corePoolSize数量的线程。
什么是拒绝策略呢?当等待队列也已经排满了,再也塞不下新的任务了同时,线程池的max也到达了,无法接续为新任务服务,这时我们需要拒绝策略机制合理的处理这个问题。
JDK内置了四种常用的拒绝策略:
标签:说明 支持 keep src 长度 工厂方法 默认值 大于 stat
原文地址:https://www.cnblogs.com/simon-1024/p/12215548.html