Future基本知识不做介绍,主要是简单的实现方式
1.创建线程池
FixedThreadPool 与 CachedThreadPool 特性对比
特性 | FixedThreadPool | CachedThreadPool |
重用 | FixedThreadPool 与 CacheThreadPool差不多,也是能 reuse 就用,但不能随时建新的线程 | 缓存型池子,先查看池中有没有以前建立的线程,如果有,就 reuse ;如果没有,就建一个新的线程加入池中 |
池大小 | 可指定 nThreads,固定数量 | 可增长,最大值 Integer.MAX_VALUE |
队列大小 | 无限制 | 无限制 |
超时 | 无 IDLE | 默认 60 秒 IDLE |
使用场景 | 所以 FixedThreadPool 多数针对一些很稳定很固定的正规并发线程,多用于服务器 | 大量短生命周期的异步任务 |
结束 | 不会自动销毁 | 注意,放入 CachedThreadPool 的线程不必担心其结束,超过 TIMEOUT 不活动,其会自动被终止。 |
最佳实践
FixedThreadPool 和 CachedThreadPool 两者对高负载的应用都不是特别友好。
CachedThreadPool 要比 FixedThreadPool 危险很多。
如果应用要求高负载、低延迟,最好不要选择以上两种线程池:
- 任务队列的无边界:会导致内存溢出以及高延迟
- 长时间运行会导致
CachedThreadPool
在线程创建上失控
因为两者都不是特别友好,所以推荐使用 ThreadPoolExecutor
,它提供了很多参数可以进行细粒度的控制。
- 将任务队列设置成有边界的队列
- 使用合适的
RejectionHandler
- 自定义的RejectionHandler
或 JDK 提供的默认 handler 。 -
如果在任务完成前后需要执行某些操作,可以重载
beforeExecute(Thread, Runnable) afterExecute(Runnable, Throwable)
- 重载 ThreadFactory ,如果有线程定制化的需求
-
在运行时动态控制线程池的大小
创建共享的线程池,本实例使用了ThreadPoolExecutor 线程池
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CommonThreadPoolExecutor {
public static final ThreadPoolExecutor futureThreadPool; // 并发共用线程
static {
//线程池维护线程的最少数量,核心线程数
final int PARTNER_COREPOOLSIZE = 40;
//线程池维护线程的最大数量
final int PARTNER_MAXINUMPOOLSIZE = 200;
//线程池维护线程所允许的空闲时间
final long PARTNER_KEEPALIVETIME = 0;
//线程池维护线程所允许的空闲时间的单位
final TimeUnit PARTNER_UNIT = TimeUnit.SECONDS;
//线程池所使用的缓冲队列,这里队列大小为20
final BlockingQueue<Runnable> PARTNER_WORKQUEUE = new ArrayBlockingQueue<Runnable>(20);
//线程池对拒绝任务的处理策略:AbortPolicy为抛出异常;CallerRunsPolicy为重试添加当前的任务,他会自动重复调用execute()方法;DiscardOldestPolicy为抛弃旧的任务,DiscardPolicy为抛弃当前的任务
final ThreadPoolExecutor.CallerRunsPolicy PARTNER_HANDLER = new ThreadPoolExecutor.CallerRunsPolicy();
futureThreadPool = new ThreadPoolExecutor(PARTNER_COREPOOLSIZE, PARTNER_MAXINUMPOOLSIZE, PARTNER_KEEPALIVETIME, PARTNER_UNIT, PARTNER_WORKQUEUE, PARTNER_HANDLER);
}
}
2.业务实现
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class CommonService {
public static void main(String[] args) {
// 使用固定数量的线程池,避免高并发导致服务器线程无限制的增长
ThreadPoolExecutor executor = CommonThreadPoolExecutor.futureThreadPool;
try {
// 主线程优先拿到最先完成的任务的返回值,而不管它们加入线程池的顺序。
CompletionService<Object> completionService = new ExecutorCompletionService<Object>(executor);
Future<Object> future = null;
// 返回结果的集合
List<Future> results = new ArrayList<>();
// 需要并发处理的数据集
String ids = "1,2,3";
for (String pid : ids.split(",")) {
final String id = pid;
future = completionService.submit(new Callable<Object>() {
@Override
public Object call() {
// 处理实际业务
System.out.println(id);
// 返回处理的结果
return new Object();
}
});
// 等待返回结果
results.add(future);
}
Object obj = null;
for (Future fut : results) {
// fut.get() 得到返回的结果
obj = (Object)fut.get();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
注:使用线程能很大的提升相应速度,但需要谨慎使用,避免服务器压力过高