线程池使用案例
线程池创建方式
一、Executors
1、newFixedThreadPool
1.1 创建固定大小的线程池。
1.2 创建方式:ExecutorService fixedTp= Executors.newFixedThreadPool(int count);
1.3 适用场景:执行长期的任务
2、newCachedThreadPool
2.1 创建一个不限线程数上限的线程池,任何提交的任务都将立即执行
2.2 创建方式:ExecutorService cachedTp = Executors.newCachedThreadPool();
2.3 适用场景:执行很多短期异步的小程序或者负载较轻的服务器
3、newSingleThreadExecutor
3.1 创建只有一个线程的线程池,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
3.2 创建方式:ExecutorService SingleTp = Executors.newSingleThreadExecutor();
3.3 适用场景:一个任务一个任务执行的场景
4、newScheduledThreadPool
4.1 创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行。
4.2 创建方式:ScheduledExecutorService schedTp= Executors.newScheduledThreadPool(5);
4.3 适用场景:周期性执行任务的场景
创建一个定长的线程池,线程池可以周期性或延迟执行如下:
(1) scheduledTp.schedule(r1, 5, TimeUnit.SECONDS);//延迟3秒后执行r1
(2) scheduledTp.scheduleAtFixedRate(r2,5,3,TimeUnit.SECONDS);//延迟5秒后每3秒执行一次r2
二、ThreadPoolExecutor
Executors创建线程池的快捷方法,实际上是调用了ThreadPoolExecutor的构造方法
public ThreadPoolExecutor(
int corePoolSize, // 线程池长期维持的线程数,即使线程处于Idle状态,也不会回收。
int maximumPoolSize, // 线程数的上限
long keepAliveTime, TimeUnit unit, // 超过corePoolSize的线程的idle时长,
// 超过这个时间,多余的线程会被回收。
BlockingQueue<Runnable> workQueue, // 任务的排队队列
ThreadFactory threadFactory, // 新线程的产生方式
RejectedExecutionHandler handler) // 拒绝策略
举例:
ExecutorService executorService = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(512), // 使用有界队列,避免OOM
new ThreadPoolExecutor.DiscardPolicy());//DiscardPolicy 对溢出队列的任务不做处理,直接忽略
其他拒绝策略
AbortPolicy 抛出RejectedExecutionException(默认)
DiscardOldestPolicy 丢弃执行队列中最老的任务,尝试为当前提交的任务腾出位置
CallerRunsPolicy 直接由提交任务者执行这个任务
三、两种线程池创建方式的异同
1、Executors.newXXXThreadPool()快捷方法创建线程池,这种方式使用无界的任务队列,容易造成OOM
建议使用ThreadPoolExecutor的构造方法手动指定各项参数。
2、提交任务的类型分为Runnable和Callable 二者区别如下:
2.1 方法签名不同,void Runnable.run(), V Callable.call() throws Exception
2.2 是否允许有返回值,Callable允许有返回值
2.3 是否允许抛出异常,Callable允许抛出异常。
提交任务的方式:
Future<T> submit(Callable<T> task) 有返回值
void execute(Runnable command) 无返回值
Future<?> submit(Runnable task) 虽然返回Future,但是其get()方法总是返回null
四、返回值
1、获取单个结果
用submit()向线程池提交任务后会返回一个Future,Future.get()方法能够阻塞等待执行结果,
get(long timeout, TimeUnit unit)方法可以指定等待的超时时间。
2、获取多个结果
如果向线程池提交了多个任务,要获取这些任务的执行结果,可以依次调用Future.get()获得。
对于这种场景,使用ExecutorCompletionService,该类的take()方法总是阻塞等
待某一个任务完成,然后返回该任务的Future对象。向CompletionService批量提交任务后,
只需调用相同次数的CompletionService.take()方法,就能获取所有任务的执行结果。
List<Object>lists=new ArrayList<>();
ExecutorService executorService = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(512), // 使用有界队列,避免OOM
new ThreadPoolExecutor.DiscardPolicy());
CompletionService<Object> cs= new ExecutorCompletionService<Object>(executorService );
for (Callable<Object> s : lists)// 提交所有任务
cs.submit(s);
int n = lists.size();
for (int i = 0; i < n; ++i) {// 获取每一个完成的任务
Object r = cs.take().get();
if (r != null)
//一些操作
}
五、一个简单实例
模拟发送消息
public class ExecutorTest {
//核心线程数
int poolSize = Runtime.getRuntime().availableProcessors() * 2;
//任务队列
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(512);
//拒绝策略
RejectedExecutionHandler policy = new ThreadPoolExecutor.DiscardPolicy();
//创建线程池
ExecutorService service = new ThreadPoolExecutor(poolSize, poolSize,
0, TimeUnit.SECONDS, queue, policy);
public static void main(String[] args) {
ExecutorTest pmt = new ExecutorTest();
//待接收人
List<Integer> receivers = new ArrayList<>();
for (int i = 0; i < 100; i++) {
receivers.add(i);
}
String content = "群发消息_测试代码";
int successCount = pmt.sendMsg(receivers, content); //使用线程池处理发送任务
System.out.println("共有[" + receivers.size() + "]接收者,发送成功["+successCount+"]");
}
public int sendMsg(List<Integer> data, String content) {
long begin = System.nanoTime();//开始时间
AtomicInteger ai = new AtomicInteger(0);//原子性计数
// 获取多个返回结果
CompletionService<Integer> ecs = new ExecutorCompletionService<>(service);
//循环发送消息
for (int i = 0, len = data.size(); i < len; i++) {
Integer dataReceiver = data.get(i);
ecs.submit(() -> {
//模拟调用相对比较耗时的发送消息接口
Thread.sleep(100);
//发送消息
int result = sendMsg(dataReceiver, content);
System.out.println("接收者["+dataReceiver + "], 执行结果["+result+"]");
return result;
});
}
System.out.println("-----" + (System.nanoTime() - begin) / 1000_000d + "-----");
//循环获得执行结果
int length = data.size();
for (int i = 0; i < length; i++) {
try {
int resultStatus = ecs.take().get();
if (resultStatus == 0) { //发送成功
ai.incrementAndGet();
}
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("发送消息结束,耗时:" + (System.nanoTime() - begin) / 1000_000d);
service.shutdown();//关闭线程池
try {
if (!service.awaitTermination(30, TimeUnit.SECONDS)) {
System.out.println(" 到达指定时间,还有线程没执行完,不再等待,关闭线程池!");
// 调用 shutdownNow 取消正在执行的任务
service.shutdownNow();
// 再次等待 60 s,如果还未结束,可以再次尝试,或则直接放弃
if (!service.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("线程池任务未正常执行结束");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return ai.get();//返回成功发送的次数
}
//完成发送消息
private int sendMsg(Integer receiver, String content) {
if (receiver % 2 == 0) {//模拟被2整除,即为发送成功
return 0;
}
return 1;
}
}