好文:
https://www.jianshu.com/p/76ddf9745c43
https://blog.csdn.net/u011479540/article/details/51867886
线程池优雅平滑关闭
附上一个平缓关闭池的方法(摘自rocketmq ThreadUtil)
/**
* 优雅关闭线程池,以标准输出流的方式输出提示信息
* @param executor 需要关闭的线程池
* @param timeout 最大等待时间(毫秒),超时则直接调用 executor 的 shutdownNow() 方法强行关闭
* @param name 线程池名称
* @param printRate 打印的时间间隔
*/
public static void shutdownGracefully(ExecutorService executor, long timeout, String name, long printRate) {
long start = System.currentTimeMillis();
executor.shutdown();
try {
// 阻塞等待线程池中任务都完成了才结束
while (!executor.awaitTermination(printRate, TimeUnit.MILLISECONDS)) {
System.out.println(String.format("[%s] WARN %s Shuting down %s", DateUtil.format(FormatType.DATE_TIME_MS), name, executor));
if (System.currentTimeMillis() - start >= timeout) {
System.out.println(String.format("[%s] WARN %s shutdown timeout[%s], force terminate! %s",
DateUtil.format(FormatType.DATE_TIME_MS), name, timeout, executor));
executor.shutdownNow();
break;
}
}
} catch (InterruptedException e) {
System.out.println(String.format("[%s] WARN catch InterruptedException and shutdown %s now!", DateUtil.getCurrentDate(), name));
executor.shutdownNow();
Thread.currentThread().interrupt();
}
System.out.println(String.format("[%s] INFO shutdown %s, elapse time %s",
DateUtil.format(FormatType.DATE_TIME_MS), name, System.currentTimeMillis() - start));
}
题外话:定时任务,可以用线程池“ScheduleThreadPoolExcutor”实现。
前提:如果并发量比较小,只是要求在子线程执行部分业务,那可以考虑使用
Executors.newSingleThreadExecutor()
java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
线程池不建议使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方
式让写的代码用户更加明确线程池的运行规则,养成规避资源耗尽的风险习惯。
说明: Executors 返回的线程池对象的弊端如下:
a) FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
b) CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4,20,600,TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(50),new ThreadPoolExecutor.DiscardOldestPolicy()));
参数含义:(核心线程,最大线程,线程存活时间,时间单位,阻塞队列【不要用默认参数,默认为Interger.MAX_VALUE】,任务多出队列后遗弃线程的策略)
下面是一个example:
/**
* @ClassName FirstController
* @Description 测试
* @Author lkun
**/
@ApiIgnore
@RestController
public class FirstController {
private final static Logger logger = LoggerFactory.getLogger(FirstController.class);
private ExecutorService threadPool;
{
//threadPool = Executors.newSingleThreadExecutor();
threadPool = new ThreadPoolExecutor(2, 20, 10,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(900),new ThreadPoolExecutor.DiscardOldestPolicy());
}
@Autowired
IrisDataService irisDataService;
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
IrisDataRepository irisDataRepository;
@RequestMapping(value = "/helloWorld",method = RequestMethod.GET)
public String helloWorld() throws Exception {
for(int i = 0 ; i < 1000; i++) {
final int num = i;
threadPool.execute(new Runnable(){
@Override
public void run(){
try {
Thread.sleep(2000);
System.out.println("test : " + num);
}catch(Exception e){
logger.error("error thread:" + e.getMessage(),e);
}
}
});
}
System.out.println("Thread run end!");
return "true";
}
@RequestMapping(value = "/testMethod",method = RequestMethod.GET)
public String testMethod() {
logger.info("test info");
logger.debug("test debug");
return "test success11ger";
}
}
上述代码,由于循环1000次速度极快,但线程池只允许阻塞队列900个,那么真实情况会如何执行?
A:循环一共被执行920次,20是最大线程数,即拿到执行权的线程;900是其他拿不到执行权排队中的线程,其余的80条就被遗弃策略给丢掉了....
workQueue(工作队列):用于保存等待执行的任务的阻塞队列。
-
ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO(先进先出)原则对任务进行排序。使用该队列,线程池中能创建的最大线程数为maximumPoolSize。
-
LinkedBlockingQueue:基于链表结构的无界阻塞队列,按FIFO(先进先出)原则对任务进行排序,吞吐量高于ArrayBlockingQueue。使用该队列,线程池中能创建的最大线程数为corePoolSize。静态工厂方法 Executor.newFixedThreadPool()使用了这个队列。
-
SynchronousQueue:一个不存储元素的阻塞队列。添加任务的操作必须等到另一个线程的移除操作,否则添加操作一直处于阻塞状态。静态工厂方法 Executor.newCachedThreadPool()使用了这个队列。
-
PriorityBlokingQueue:一个支持优先级的无界阻塞队列。使用该队列,线程池中能创建的最大线程数为corePoolSize。