承继上一篇,上一篇主要分析了下ThreadPoolExecutor中的大概执行逻辑,这一篇主要分析一下内部参数的写一写测试程序。
首先来看里面的最终的默认构造方法,分析其参数,以及其流程
//Executors中的四个构造方法最终调用的是下面一个初始化方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
//1. corePoolSize 核心线程数,所谓核心线程数,就是会一直存活的线程数,即使期间没有需要执行的任务
// 因此,当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理任务,并不是交由现有
// 的线程处理,allowCoreThreadTimeout(true),默认为false,可以设置核心线程超时关闭
//2. maximumPoolSize,当线程数>=核心线程数时,根据上一篇的分析,
//2.1如果此时任务队列没有满,此时会加入到任务队列中,等待任务调度执行,
//2.2如果此时任务队列已满,此时会判断线程数与maximumPoolSize的大小
// 2.2.1如果此时线程数没有达到maximumPoolSize,那么就会创建新的线程执行任务
// 2.2.2如果此时线程数等于maximumPoolSize,那么就标志着超出线程池的处理范围,线程池会拒绝掉,具体
// 拒绝策略下面再分析
//3. keepAliveTime,当线程空闲时间达到keepAliveTime时,改线程会退出,直至线程数目为corePoolSize,
// 如果allowCoreThreadTimeout设置为true的话,则所以线程均会退出直至线程数量为0.
//可总结下线程池的任务执行策略
//1.当线程数小于核心线程数时,创建线程
//2.当线程数等于核心线程数时,且此时任务队列未满,将任务放入任务队列
//3.当线程数大于等于核心线程数,且任务队列已满
// 3.1若线程数小于最大线程数,创建线程
// 3.2若线程数等于最大线程数,拒绝任务
测试AbortPolicy
//测试线程池拒绝策略
public class TestExceptionThread {
private static int totalNum = 2;
//ThreadPoolExecutor 提供了四种拒绝策略
//1.AbortPolicy,顾名思义,直接拒绝掉本次线程,抛出RejectedExecutionException
//测试该种拒绝策略
public static void main(String[] args){
//构建出一个只有3个线程池大小的service
ExecutorService service = Executors.newFixedThreadPool(totalNum);
for(int i = 0; i < 5; i++){
final int j = i;
System.out.println("线程"+j+"创建");
Runnable runnable = new Runnable() {
@Override
public void run() {
try{
System.out.println("线程"+j+"执行中");
Thread.sleep(50000);
}catch (Exception e){
e.printStackTrace();
}
}
};
service.submit(runnable);
}
}
}
结果如下,并没有抛出异常?奇怪了。。。怎么回事,难不成线程一直在加入,没有执行到抛出异常的范围.
线程0创建
线程1创建
线程0执行中
线程2创建
线程3创建
线程4创建
线程1执行中
问题其实出在初始化步骤,可以看见初始化时使用了LinkedBlockingQueue的默认大小,这样的话,队列大小为Integer.MAX_VALUE,这种情况下,最大线程数就失去作用了,根据上述步骤,此时核心线程数虽然已满,但是队列确很大,因此全部加入了队列任务中去了,最终会被任务调度执行,可见,队列的大小设置也是有讲究的。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
再次测试,这次设置队列任务大小为2,核心线程数为2,最大线程数为3.
//测试线程池拒绝策略
public class TestExceptionThread {
private static int totalNum = 2;
//ThreadPoolExecutor 提供了四种拒绝策略
//1.AbortPolicy,顾名思义,直接拒绝掉本次线程,抛出RejectedExecutionException
//测试该种拒绝策略
public static void main(String[] args) throws Exception{
//构建出一个只有3个线程池大小的service
LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
ExecutorService service = new ThreadPoolExecutor(totalNum, totalNum+1, 0, TimeUnit.MILLISECONDS,queue);
for(int i = 0; i < 10; i++){
final int j = i;
Thread.sleep(2000);
System.out.println("线程"+j+"创建");
Runnable runnable = new Runnable() {
@Override
public void run() {
try{
System.out.println("线程"+j+"执行中");
Thread.sleep(50000);
}catch (Exception e){
e.printStackTrace();
}
}
};
service.submit(runnable);
}
}
}
测试结果如下:来看看和预期对不对.
核心线程数大小为2,最大数为3,队列任务是2.
1.线程0创建时,线程池中没有线程,执行时,线程池中运行线程数目为 1。
2.线程1创建时,核心线程为线程0,只有一个,没有到达核心线程池大小2,因此直接加入执行.此时运行线程数目为 2
3.线程2创建时,核心线程数为线程0,线程1.到达核心线程数2,因此加入到任务队列中去,任务队列中为1。
4.线程3创建时,加入到任务队列中去,因此任务队列为2,核心数还是2。
5.线程4创建时,任务队列已满了。但是此时运行的线程数只有线程0和线程1,小于最大线程数3,因此线程4被创建出并且执行了。(从结果图中可以看出)。
6.线程5创建时,运行线程数是3,并且队列已满,并且等于最大线程数了,所以直接抛出异常,因为此时线程池已经不能处理了.
7.后续的线程此时已经没办法进入了,因为线程5抛出异常了。
线程0创建
线程0执行中
线程1创建
线程1执行中
线程2创建
线程3创建
线程4创建
线程4执行中
线程5创建
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@4b33650d rejected from java.util.concurrent.ThreadPoolExecutor@43095c6c[Running, pool size = 3, active threads = 3, queued tasks = 2, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2048)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:821)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1372)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:110)
at concurrenttest.TestExceptionThread.main(TestExceptionThread.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
测试CallerRunsPolicy如下:
public class TestExceptionThread {
private static int totalNum = 2;
public static void main(String[] args) throws Exception{
//构建出一个只有3个线程池大小的service
LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
ExecutorService service = new ThreadPoolExecutor(totalNum, totalNum+1, 0,
TimeUnit.MILLISECONDS,queue,Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());
for(int i = 0; i < 10; i++){
final int j = i;
Thread.sleep(2000);
System.out.println("线程"+j+"创建");
Runnable runnable = new Runnable() {
@Override
public void run() {
try{
System.out.println(Thread.currentThread().getName()+"线程"+j+"执行中");
Thread.sleep(500000);
}catch (Exception e){
e.printStackTrace();
}
}
};
Thread.sleep(2000);
service.submit(runnable);
}
System.out.println("主线程输出");
}
}
可以看见,此时线程5在执行失败后,并没有抛出异常,而是由调用线程继续执行,也即是这边的主线程,但是此时主线程后续的输出并没出来,说明这种方式会阻塞主线程。也算是个缺点吧。在这种情况下,感觉并不是很好.
线程0创建
pool-1-thread-1线程0执行中
线程1创建
pool-1-thread-2线程1执行中
线程2创建
线程3创建
线程4创建
pool-1-thread-3线程4执行中
线程5创建
main线程5执行中
测试DiscardPolicy,其实看下源码就会发现这种处理很直接啊,什么方法都没有,这就意味着直接抛弃掉了当前线程的创建,继续执行之后的逻辑。
public class TestExceptionThread {
private static int totalNum = 2;
public static void main(String[] args) throws Exception{
//构建出一个只有3个线程池大小的service
LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
ExecutorService service = new ThreadPoolExecutor(totalNum, totalNum+1, 0,
TimeUnit.MILLISECONDS,queue,Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());
for(int i = 0; i < 10; i++){
final int j = i;
Thread.sleep(2000);
System.out.println("线程"+j+"创建");
Runnable runnable = new Runnable() {
@Override
public void run() {
try{
System.out.println(Thread.currentThread().getName()+"线程"+j+"执行中");
Thread.sleep(500000);
}catch (Exception e){
e.printStackTrace();
}
}
};
Thread.sleep(2000);
service.submit(runnable);
}
System.out.println("主线程输出");
}
}
从结果可以看出,线程5,开始就开始抛弃了,最终是回到主线程中输出日志,那么其实如果在线程6-线程9的新建过程中,线程池可以加入了,那么还是可以直接加入的。
线程0创建
pool-1-thread-1线程0执行中
线程1创建
pool-1-thread-2线程1执行中
线程2创建
线程3创建
线程4创建
pool-1-thread-3线程4执行中
线程5创建
线程6创建
线程7创建
线程8创建
线程9创建
主线程输出
public class TestExceptionThread {
private static int totalNum = 2;
public static void main(String[] args) throws Exception{
//构建出一个只有3个线程池大小的service
LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
ThreadPoolExecutor service = new ThreadPoolExecutor(totalNum, totalNum+1, 0,
TimeUnit.MILLISECONDS,queue,Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy());
for(int i = 0; i < 20; i++){
final int j = i;
System.out.println("线程" + j + "创建");
service.execute(new InnerThread("Thread "+j));
Iterator iterator = service.getQueue().iterator();
while (iterator.hasNext()){
InnerThread thread = (InnerThread) iterator.next();
System.out.println("队列线程有: "+thread.getName());
}
}
System.out.println("主线程输出");
}
}
class InnerThread implements Runnable{
private String name;
public InnerThread(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
try{
System.out.println("线程" + name + "执行中");
}catch (Exception e){
e.printStackTrace();
}
}
}
可以看见大概在线程10到线程19的时候,会出现一直剔除添加的效果。
线程0创建
线程1创建
线程Thread 0执行中
线程2创建
队列线程有: Thread 2
线程3创建
队列线程有: Thread 2
队列线程有: Thread 3
线程4创建
线程Thread 2执行中
线程Thread 3执行中
线程5创建
队列线程有: Thread 5
线程6创建
队列线程有: Thread 5
队列线程有: Thread 6
线程7创建
线程Thread 4执行中
线程Thread 5执行中
线程Thread 6执行中
线程8创建
队列线程有: Thread 8
线程9创建
队列线程有: Thread 8
队列线程有: Thread 9
线程10创建
队列线程有: Thread 8
队列线程有: Thread 9
线程11创建
队列线程有: Thread 9
队列线程有: Thread 11
线程12创建
队列线程有: Thread 11
队列线程有: Thread 12
线程13创建
队列线程有: Thread 12
队列线程有: Thread 13
线程14创建
队列线程有: Thread 13
队列线程有: Thread 14
线程15创建
队列线程有: Thread 14
队列线程有: Thread 15
线程16创建
队列线程有: Thread 15
队列线程有: Thread 16
线程17创建
队列线程有: Thread 16
队列线程有: Thread 17
线程18创建
队列线程有: Thread 17
队列线程有: Thread 18
线程19创建
队列线程有: Thread 18
队列线程有: Thread 19
主线程输出
线程Thread 10执行中
线程Thread 18执行中
线程Thread 19执行中
线程Thread 1执行中
线程Thread 7执行中
public class TestExceptionThread {
private static int totalNum = 2;
public static void main(String[] args) throws Exception{
LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
ThreadPoolExecutor service = new ThreadPoolExecutor(totalNum, totalNum+1, 0, TimeUnit.MILLISECONDS,queue,new MyReject());
for(int i = 0; i < 10; i++){
final int j = i;
Thread.sleep(2000);
System.out.println("线程"+j+"创建");
Runnable runnable = new Runnable() {
@Override
public void run() {
try{
System.out.println("线程"+j+"执行中");
Thread.sleep(50000);
}catch (Exception e){
e.printStackTrace();
}
}
};
Thread.sleep(2000);
service.execute(runnable);
}
}
static class MyReject implements RejectedExecutionHandler{
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("你被拒绝了");
}
}
}
可以看出这种方式其实比较好,可以由自己决定后续执行逻辑。
线程0创建
线程0执行中
线程1创建
线程1执行中
线程2创建
线程3创建
线程4创建
线程4执行中
线程5创建
你被拒绝了
线程6创建
你被拒绝了
线程7创建
你被拒绝了
本文总结了一下内部一写具体参数的含义,以及线程池的执行策略和拒绝策略。下一篇继续,去看看内部一写方法吧,感觉线程池需要理解的东西比较多,博客还是细分一下,将来自己回头来看时,能瞧的清楚一点。