深入理解JUC——线程池相关

线程池提供了一种限制和管理资源(包括执行一个任务)的方式。

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

  • RunnableCallable 的区别

    实现 Runnable 接口的任务不会返回结果或者抛出异常检查;实现Callable 接口的任务则可以。

  • submit()execute 方法的区别

    execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;submit()方法用于提交需要返回值的任务。线程池会返回一个 Future 类型的对象,通过这个 Future 对象可以判断任务是否执行成功,并且可以通过 Future 的 get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完

  • 线程池任务的执行— Executor 接口

    线程池任务执行的核心接口为Executor 接口,以及继承自 Executor接口的 ExecutorService ****接口。ThreadPoolExecutor和 ScheduledThreadPoolExecutor 这两个关键类实现了 ExecutorService接口。

    • Executor 框架的使用示意图
      1. 主线程首先要创建实现 Runnable 或者 Callable 接口的任务对象。
      2. 把创建完成的实现 Runnable/Callable接口的 对象直接交给 ExecutorService 执行ExecutorService.execute(...)或者也可以把 Runnable 对象或Callable 对象提交给 ExecutorService.submite(...) 执行。
      3. 如果执行 ExecutorService.submit(...)ExecutorService 将返回一个实现Future接口的对象 (FutureTask 对象)。由于 FutureTask 实现了 Runnable,我们也可以创建 FutureTask,然后直接交给 ExecutorService 执行。
      4. 最后,主线程可以执行 FutureTask.get()方法来等待任务执行完成。主线程也可以执行 FutureTask.cancel(boolean mayInterruptIfRunning)来取消此任务的执行。
  • 线程池状态

    ThreadPoolExecutor 使用 int 的高 3 位来表示线程池状态,低 29 位表示线程数量。

  • ThreadPoolExecutor 类的介绍以及核心参数

    ThreadPoolExecutorExecute 最核心的类,其构造方法如下:

    public ThreadPoolExecutor(int corePoolSize, // 核心线程数量
    			int maximumPoolSize, // 最大线程数
    			long keepAliveTime, // 当线程数大于核心线程数时,多余的空闲线程存活的最长时间
    			TimeUnit unit, // 时间单位
    			BlockingQueue<Runnable> workQueue, // 任务队列,用来储存等待执行任务的队列
    			ThreadFactory threadFactory, // 线程工厂,用于创建线程
    			RejectedExecutionHandler handler // 拒绝策略,当提交线程过多来不及处理时,根据拒绝策略来处理任务
    )
    
    • 运行机制

      1. 线程池中刚开始的时候没有线程,当一个任务提交之后,线程池会创建一个新的线程来执行任务
      2. 当线程数量达到 corePoolSize 且没有线程空闲的时候,再加入的新任务会进入到 workQueue 中排队等待,直到有空闲的线程
      3. 如果workQueue 选择的是有界队列,那么当等待的任务数量超过了workQueue 的大小时,会创建 maximumPoolSize-corePoolSize 数目的临时线程来执行任务。
      4. 如果线程到达 maximumPoolSize 仍然有新任务这时会执行拒绝策略。ThreadPoolTaskExecutor 定义一些策略:
      • ThreadPoolExecutor.AbortPolicy :抛出 RejectedExecutionException来拒绝新任务的处理。
      • ThreadPoolExecutor.CallerRunsPolicy :让调用者自己执行本次任务。
      • ThreadPoolExecutor.DiscardPolicy :不处理新任务,直接丢弃掉。
      • ThreadPoolExecutor.DiscardOldestPolicy : 此策略将丢弃最早的未处理的任务请求。
      1. 超过corePoolSize 的临时线程如果一段时间内没有任务做,则需要结束线程。这个时间由 TimeUnit和keepAliveTime 决定。
    • 常见的线程池

      • FixedThreadPool

        public static ExecutorService newFixedThreadPool(int nThreads) {
        		 return new ThreadPoolExecutor(nThreads, nThreads,
        						 0L, TimeUnit.MILLISECONDS,
        						 new LinkedBlockingQueue<Runnable>());
        }
        

        特点: corePoolSize == maximumPoolSize (没有救急线程被创建),因此也无需超时时间阻塞队列是无界的,可以放任意数量的任务;它存在的问题是:运行中的 FixedThreadPool(未执行 shutdown()或 shutdownNow())不会拒绝任务,在任务比较多的时候会导致 OOM(内存溢出)。

        <aside> 💡 适用于任务量已知,相对耗时的任务

        </aside>

      • CachedThreadPool

        public static ExecutorService newCachedThreadPool() {
        			 return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
        							 60L, TimeUnit.SECONDS,
        							 new SynchronousQueue<Runnable>());
        }
        

        特点:

        核心线程数量为0,最大线程数为Integer.MAX_VALUE,临时线程的生成时间为60s,意味着全部都是临时线程(60s 后可以回收),且临时线程可以无限创建。在极端情况下,这样会导致耗尽 CPU 和内存资源。

        <aside> 💡 整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲 1分钟后释放线程。 适合任务数比较密集,但每个任务执行时间较短的情况。

        </aside>

      • SingleThreadExecutor

        public static ExecutorService newSingleThreadExecutor() {
        		 return new FinalizableDelegatedExecutorService
        				(new ThreadPoolExecutor(1, 1,
        						 0L, TimeUnit.MILLISECONDS,
        						 new LinkedBlockingQueue<Runnable>()));
        }
        

        特点:

        创建的 SingleThreadExecutor的 corePoolSize和 maximumPoolSize都被设置为 1,其他参数和 FixedThreadPool相同。线程数固定为 1,任务数多于 1 时,会放入无界队列排队。任务执行完毕,这唯一的线程也不会被释放。SingleThreadExecutor使用无界队列作为线程池的工作队列会对线程池带来的影响与 FixedThreadPool相同。说简单点就是可能会导致 OOM。

        <aside> 💡 希望多个任务排队执行。

        </aside>

      • ScheduledThreadPoolExecutor

    • 关闭线程池

      Shutdown() :线程池状态便为 shutdown,不会接收新的任务,但已提交的任务会执行完,此方法不会阻塞调用线程的执行。

      ShutdownNow() :线程池状态变为 stop,不会接收新的任务,会将队列中等待的任务返回,并用interrupt的方式中断正在执行中的任务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值