使用ExecutorService线程池实例执行与结果
为了更好的学习ExecutorService线程池工作原理,我们首先创建几个与线程相关的类。
一、ThreadPoolExecutorServiceHolder:
package com.acorn.core.component;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
//@Component
@Slf4j
public class ThreadPoolExecutorServiceHolder {
ThreadFactory namedThreadFactory;
ExecutorService executorService;
//@PostConstruct
public void init() {
int cpuCoreNum = Runtime.getRuntime().availableProcessors();
log.info(":线程池初始化-当前CPU核心数{}",cpuCoreNum);
int corePoolSize = cpuCoreNum*2+1;
int maximumPoolSize = 2*corePoolSize+1;
long keepAliveTime = 60 * 1000L;
log.info(":{}线程池核心数{},最大线程数{}",this.getClass().getName(),corePoolSize,maximumPoolSize);
this.namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("ThreadExecutorServiceComponent-%d").build();
this.executorService = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(maximumPoolSize), namedThreadFactory);
}
public void execute(Runnable run) {
executorService.execute(run);
}
public <T> Future<T> submit(Callable<T> run) {
Future<T> f = executorService.submit(run);
return f;
}
public boolean isShutdown(){
return this.executorService.isShutdown();
}
}
二、
package com.acorn.core.component;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TestExcutorService {
public static void main(String[] args) {
ThreadPoolExecutorServiceHolder holder = new ThreadPoolExecutorServiceHolder();
holder.init();
TestExcutorService s = new TestExcutorService();
for (int i = 1; i < 101; i++) {
MyRunner r = s.new MyRunner(i);
holder.execute(r);
}
}
public class MyRunner implements Runnable {
private int num;
public MyRunner(int num) {
this.num = num;
}
@Override
public void run() {
log.info(String.format(":执行线程%s,i:%s",Thread.currentThread().getName(),this.num));
}
}
}
执行
ThreadPoolExecutorServiceHolder - :线程池初始化-当前CPU核心数4
ThreadPoolExecutorServiceHolder - :com.acorn.core.component.ThreadPoolExecutorServiceHolder线程池核心数9,最大线程数19
10:47:42.563 TestExcutorService - :执行线程ThreadExecutorServiceComponent-1,i:2
10:47:42.563 TestExcutorService - :执行线程ThreadExecutorServiceComponent-0,i:1
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-2,i:3
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-0,i:11
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-1,i:10
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-2,i:12
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-0,i:13
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-2,i:14
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-2,i:17
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-1,i:16
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-0,i:15
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-0,i:19
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-2,i:20
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-1,i:18
......
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-0,i:76
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-0,i:77
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-2,i:78
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-0,i:79
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-2,i:85
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-0,i:86
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-2,i:87
10:47:42.579 TestExcutorService - :执行线程ThreadExecutorServiceComponent-3,i:4
10:47:42.594 TestExcutorService - :执行线程ThreadExecutorServiceComponent-3,i:99
10:47:42.594 TestExcutorService - :执行线程ThreadExecutorServiceComponent-0,i:98
10:47:42.594 TestExcutorService - :执行线程ThreadExecutorServiceComponent-2,i:97
10:47:42.594 TestExcutorService - :执行线程ThreadExecutorServiceComponent-3,i:100
10:47:42.594 TestExcutorService - :执行线程ThreadExecutorServiceComponent-4,i:5
10:47:42.594 TestExcutorService - :执行线程ThreadExecutorServiceComponent-5,i:6
10:47:42.594 TestExcutorService - :执行线程ThreadExecutorServiceComponent-6,i:7
10:47:42.594 TestExcutorService - :执行线程ThreadExecutorServiceComponent-7,i:8
10:47:42.594 TestExcutorService - :执行线程ThreadExecutorServiceComponent-8,i:9
10:47:42.610 TestExcutorService - :执行线程ThreadExecutorServiceComponent-1,i:65
从线程的执行效果来看,当我们把新的线程任务提交到ExecutorService时:即holder.execute®;,此时ExecutorService的execute方法会检查线程池,如果为空,还没有线程创建,则会创建一个线程(new Worker)并放入HashSet中,创建成功即时开启线程t.start();至于线程什么时候run则交给cpu调度。于是我们看到10:47:42.563时刻有0,1标号的worker创建完成并被立即调度。10:47:42.579时刻2,3号创建完成也被加入到HashSet中并被立即调度。在上述执行效果来看0,1,2号线程被调度效率较高。从这次执行结果来看只创建了9个核心线程,就完成了100次任务。实际上并不是每次都创建9个核心线程,有时会等于9,有时会大于9,有时会报线程数量超限错误。
例如有时出现:
rejected from java.util.concurrent.ThreadPoolExecutor@dd3b207[Running, pool size = 19, active threads = 1, queued tasks = 0, completed tasks = 46]
只完成了46个任务,并且执行顺序杂乱无章。那么问题来了,在有界线程池中想要执行完所有的任务,该如何做?
其实很简单,就是在新创建任务时稍微睡眠一下让CPU完成执行线程的系统条件即可。
例如在
for (int i = 1; i < 101; i++) {
MyRunner r = s.new MyRunner(i);
Thread.sleep(10);//插入的睡眠代码
holder.execute(r);
}
增加Thread.sleep(10);后,运行程序就会全部执行完所有任务,并且按顺序执行。在实际应用中需要程序员自己判断完成一个任务所需要大致时间来设置睡眠时间。譬如一个http请求大约需时500毫秒,那就设置睡眠时间为250毫秒。
增加睡眠代码的效果:
11:28:53.861 ThreadPoolExecutorServiceHolder线程池核心数9,最大线程数19
11:28:53.892 MyRunner - 执行线程ThreadExecutorServiceComponent-0,i:1
11:28:53.908 MyRunner - 执行线程ThreadExecutorServiceComponent-1,i:2
11:28:53.923 MyRunner - 执行线程ThreadExecutorServiceComponent-2,i:3
11:28:53.939 MyRunner - 执行线程ThreadExecutorServiceComponent-3,i:4
11:28:53.955 MyRunner - 执行线程ThreadExecutorServiceComponent-4,i:5
11:28:53.970 MyRunner - 执行线程ThreadExecutorServiceComponent-5,i:6
11:28:53.986 MyRunner - 执行线程ThreadExecutorServiceComponent-6,i:7
11:28:54.001 MyRunner - 执行线程ThreadExecutorServiceComponent-7,i:8
11:28:54.017 MyRunner - 执行线程ThreadExecutorServiceComponent-8,i:9
...
11:28:55.441 MyRunner - 执行线程ThreadExecutorServiceComponent-1,i:92
11:28:55.456 MyRunner - 执行线程ThreadExecutorServiceComponent-2,i:93
11:28:55.472 MyRunner - 执行线程ThreadExecutorServiceComponent-3,i:94
11:28:55.488 MyRunner - 执行线程ThreadExecutorServiceComponent-4,i:95
11:28:55.503 MyRunner - 执行线程ThreadExecutorServiceComponent-5,i:96
11:28:55.519 MyRunner - 执行线程ThreadExecutorServiceComponent-6,i:97
11:28:55.534 MyRunner - 执行线程ThreadExecutorServiceComponent-7,i:98
11:28:55.550 MyRunner - 执行线程ThreadExecutorServiceComponent-0,i:99
11:28:55.566 MyRunner - 执行线程ThreadExecutorServiceComponent-8,i:100
结论:需要在任务创建后设置一定的睡眠时间以便让CPU准备好线程执行条件
Callable
一些情况下我们需要获取线程执行结果,ExecutorService.submit就是这样的方法,它允许提交一个指定返回对象的任务。submit的实现是用FutureTask去封装程序员提交的Callable(下文简写为客户Callable)。当FutureTask执行run时调用客户Callable的call方法获得返回值。ExecutorService.submit方法其核心还是execute。
千万不要以为ThreadPoolExecutor(ExecutorService的实现类)会帮你处理线程通讯的问题。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
这ctl仅仅是为了判断在ThreadPoolExecutor中所创建的线程在workQueue池中的空闲数量,如果有空闲的Worker,它就拿出来包装一下你提交进来的Runnable并设置执行状态。
处理线程通讯的问题还是要交给程序员来处理。