线程池:Executor框架

无限制创建线程的不足

  1. 线程生命周期的开销非常高。线程的创建并不是没有代价的。根据平台的不同,实际的开销也有所不同,但是线程的创建过程都会需要时间,延迟处理的请求,并且需要JVM和操作系统提供一些辅导操作。如果请求的到达率非常高且请求的处理过程是轻量级的,例如大多数服务器应用程序就是这种情况,那么为每个请求创建一个新线程将消耗大量的计算资源。
  2. 资源消耗。活跃的线程会消耗资源,尤其是内存。如果可运行的线程数量多于可用处理器的数量,那么有些线程将会闲置。大量的空闲的线程会占用许多内存,给垃圾回收器带来压力,而且大量线程在竞争CPU资源时还将产生其他的性能开销。如果你已经拥有足够多的线程使所有cpu保持忙碌状态,那么再创建更多的线程反而会降低性能。
  3. 稳定性。在可创建线程的数量上存在一个限制。这个限制将随着平台的不同而不同,并且受多个因素制约,包括jvm的启动参数、Thread构造函数中请求的栈大小,以及底层操作的限制等。如果破坏这些限制,那么很可能抛出OutOfMemoryError异常,要想从这种错误中恢复过来是非常危险的,更简单的办法是通过构造程序来避免超出这种限制。

以上摘抄自《JAVA并发编程实战》

由以上问题看出,无限创建线程的危害,在jdk5以后提供Executor框架有效的为使用者提供了一个可用的方案

  • Executor基于生产者-消费者模式,提交任务相当于生产者(生成待完成的工作单元),执行任务的线程则相当于消费者(执行这些工作单元)。
  • 简单使用:
    /**
     * 线程池测试
     */
    public class ThreadPoolExecutorTest {
    
        private static final Executor executor = Executors.newFixedThreadPool(10);
    
        /**
         * 线程池
         *
         * @param args
         */
        public static void main(String[] args) {
            for (int i = 0; i <= 100; i++) {
                int finalI = i;
                executor.execute(() -> System.out.println("执行线程" + finalI));
            }
        }
    }
    
    打印结果打印100次,执行顺序也不是按照顺序执行,说明是单独线程每个运行的且执行100次
  • Executors工具类源码可以创建多种线程池,这里主要和策略有关系(new的方式创建对应线程池也可以设置策略)源码比较多请参考:Executors创建的4种线程池的使用

线程池主要是通过状态参数进行控制的,任务执行其实也是队列的一种实现。有兴趣的可以参考博客看一看雨源码:JDK线程池源码分析之ThreadPoolExecutor,我觉得讲的还是比较通俗易懂

到这里我就想到Spring task定时任务了,所以专门百度了一下,然后顺带看了一点源码,大概啰嗦一下

  • Spring在任务调度时默认使用的还是ScheduledThreadPoolExecutor可以查看部分源码ThreadPoolTaskScheduler类:

    
    	@UsesJava7
    	@Override
    	protected ExecutorService initializeExecutor(
    			ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
    
    		this.scheduledExecutor = createExecutor(this.poolSize, threadFactory, rejectedExecutionHandler);
    
    		if (this.removeOnCancelPolicy) {
    			if (setRemoveOnCancelPolicyAvailable && this.scheduledExecutor instanceof ScheduledThreadPoolExecutor) {
    				((ScheduledThreadPoolExecutor) this.scheduledExecutor).setRemoveOnCancelPolicy(true);
    			}
    			else {
    				logger.info("Could not apply remove-on-cancel policy - not a Java 7+ ScheduledThreadPoolExecutor");
    			}
    		}
    
    		return this.scheduledExecutor;
    	}
    
    
  • Spring在使用task调度时每个任务默认是单线程跑的,需要单独设置线程池具体设置方式参考:Spring Boot 定时任务单线程和多线程spring task定时器的配置使用

最后吐槽一波,翻了好久,Spring task源码读着还是很吃力的,希望自己能多提升一点能力吧。另外没有详细介绍线程池,是因为我觉得分享的博客已经讲得非常到位了,我也没必要去copy,见谅吧。

猜你喜欢

转载自blog.csdn.net/qq_28325291/article/details/82859913