前言
java开源长了, 代码久了,网上对于线程那是众说纷纭,一直萦绕我心头的,jdk线程池好还是spring线程池好?
结果发现,spring生命周期管理的线程池,其底层就是私有ThreadPoolExecutor类,spring(具体管理ThreadPoolTaskExecutor类)只是对其一种封装调用而已;
而我们日常使用jdk线程池 - Executor框架, ThreadPoolExecutor是Executor框架的核心实现类;
说到底,jdk线程池与spring线程池都是在使用ThreadPollExecutor基础上开发;
JDK线程
古老的extend,implment Runnable的实现线程的方式就不说了,也就是测试类写写
科普下, jdk8 lambda格式方便快捷的使用
new Thread(() -> System.out.println("线程打印...")).start();
ThreadPollExecutor
炒的火热jdk线程池的的核心实现类。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
- corePoolSize: 核心线程数。默认情况下线程池是空的,只有有任务提交的时候才会去创建线程。当运行的线程少于corePoolSize的时候,新建任务将创建核心线程,如果的等于或者大于corePoolSize的时候将不会创建核心线程,创建普通线程。可以调用prestartAllcoreThread的方法来提前创建并启动所有的核心线程。
- maximumPoolSize:线程池所允许的最大的线程数量。当任务队列满了的话线程数小于maximumPoolSize的值的话,新建任务还是会创建新的线程的。
- keepAliveTime : 非核心线程闲置的超时时间,第一是非核心的线程第二是闲置状态。调用allowCoreThreadTimeOut的时候该设置也会作用再核心的线程上面。
- TimeUnit : 超时的时间单位。DAYS(天),HOURS(小时),MINUTES(f分钟),SECONDS(秒),MILLISECONDS(毫秒).
- workQueue : 任务队列(阻塞队列)。当前线程书大于corePoolSize的大小的时候,会将任务加入阻塞队列里面。该队列是BlockingQueue类型的。
- ThreadFactory : 线程工场。我们可以自己去定义。
- RejectedExecutionHandler : 饱和策略。这是当任务队列和线程数都满了的时候所采取的的对应策略默认是AbordPolicy表示无法处理新的任务,并抛出RejectedExecutionException异常。
- CallerRunsPolicy : 用调用者所在的线程来处理任务。
- DisCardPolicy : 不能执行任务并将任务删除。
- DisCardOldesPolicy : 丢弃队列最近的任务,并执行当前的任务, 会一直执行下去。
Executore类(框架)
通过idea源码方法看到, Executors类下, 有不少快捷创建线程池方法 (idea查看类方法alt+7), 下面介绍常用的5种创建方法;
FixedThreadPool
FixedThreadPool是可重的固定线程数的线程池。
//创建FixedThreadPool
Executors.newFixedThreadPool(5);
//创建所需要的参数
public static ExecutorService newFixedThreadPool(int nThreads) {
//最后调用的都是ThreadPoolExecutor
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
我们只需要传如需要创建的线程的数量,也就是线程池的大小。我们可以看到构造方法,传入的线程的数量就是核心线程的数量。也就是FixedThreadPool会创建固定数量核心线程的线程池,并且这些核心线程不会被回收,任务超过线程的数量将存入队列中。
CachedThreadPool
该线程池是根据需要去创建线程的。
//创建缓存线程池
Executors.newCachedThreadPool();
//构造方法
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
我们可以看出,这里面的核心线程数是0,而线程的最大值是Integer.Max_VALUE。闲置超时间是一分钟。默认队列可以保证任务顺序的执行。CacheThreadPool适合大量的需要立即处理并且耗时较少的任务。
SingleThreadExecutor
该类是使用单个线程的线程池。
//创建方法
Executors.newSingleThreadExecutor();
//构造方法
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
corePoolSize和maximumPoolSize都是1,意味着SingleThreadExecutor只有一个核心线程。其他的参数和FixedThreadPool一样。如果已经创建了一个线程再来一个任务的时候会将该任务加入到任务队列里面,确保了所有任务的执行顺序。
ScheduledThreadPool
ScheduledThreadPool是一个能实现定是和周期性任务的线程池。
//创建
Executors.newScheduledThreadPool(5);
//构造方法
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
//最后还是调用的
ThreadPoolExecutor的构造方法。
当执行任务的时候,会先将任务包装成ScheduledFutrueTask并添加到DelayedWorkQueue里面,当没超过corePoolSize的时候,会创建线程,人后去DelayedWorkQueue队列里面去拿任务,并不是立即的执行。当执行完任务的时候会将ScheduledFutrueTask中的time变量改为下次要执行的时间并放回DelayedWorkQueue中。
Spring线程池
@Ansync
传统方式
springContext.xml配置管理, 其线程池生命周期由spring来管理
<bean id="threadPoolTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 核心线程数,默认为1 -->
<property name="corePoolSize" value="10" />
<!-- 最大线程数,默认为Integer.MAX_VALUE -->
<property name="maxPoolSize" value="50" />
<!-- 队列最大长度,一般需要设置值>=notifyScheduledMainExecutor.maxNum;默认为Integer.MAX_VALUE
<property name="queueCapacity" value="1000" /> -->
<!-- 线程池维护线程所允许的空闲时间,默认为60s -->
<property name="keepAliveSeconds" value="300" />
<!-- 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者 -->
<property name="rejectedExecutionHandler">
<!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->
<!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->
<!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
<!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
</property>
</bean>
注解方式
注解又分为 默认非线程池方式,线程池方式,这里已线程池方式为例子
<?xml version="1.0" encoding="UTF-8"?>
<!--Spring框架的xml标签定义文档, 可访问http://www.springframework.org/schema/task/查看最新task组件的xml标签文档-->
<beans xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.2.xsd">
<!--扫描项目实例化@Component,@Service,@Controller修饰的类-->
<context:component-scan base-package="com.your_app" />
<!-- 在代码中@Async不加参数就会使用task:annotation-driven标签定义的executor-->
<task:annotation-driven executor="myExecutor"/>
<!-- 在代码中@Async("myExecutor")可以显式指定executor为"myExecutor"-->
<task:executor id="myExecutor"
pool-size="5-25"
queue-capacity="100"
rejection-policy="CALLER_RUNS"/>
</beans>
@Async
void doSomething(String s) { //可以带参数!
// this will be executed asynchronously
}
具体配置欢迎观看
https://blog.csdn.net/caib1109/article/details/51623089
调用发现,其org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor 类, 源码查看,
初始化,也是使用ThreadPoolExecutor实现操作,具体指示设置对应的参数值而已;
好了 查看到此结束
文章涉及来源:
SpringMVC异步处理之@Async(附源代码 - 单元测试通过)
https://blog.csdn.net/caib1109/article/details/51623089
https://www.cnblogs.com/olmlo/p/4806967.html
Java多线程-五中线程池分析以及AnsyncTask源码分析
https://blog.csdn.net/jsonchumpklutz/article/details/78398534