Thread & ExecutorService & ThreadPoolExecutor 总览

简介: Thread & ExecutorService & ThreadPoolExecutor 总览ExecutorService类方法shutdown 允许已经提交的任务(尚未开始执行和已经开始执行...

Thread & ExecutorService & ThreadPoolExecutor 总览

ExecutorService

类方法

这里写图片描述

  1. shutdown
    允许已经提交的任务(尚未开始执行和已经开始执行的)继续执行
  2. shutdownNow
    尚未开始执行的任务不再执行,同时尝试终止正在执行的任务
  3. 无论是shutdown 还是shutdownNow,两个的执行都会阻止新的任务提交

  4. 一个ExecutorService一旦termination,表明没有正在执行的任务,没有等待执行的任务,也不会有新的任务可以被提交。

  5. 如果一个ExecutorService不再使用,应该调用shutdown方法来回收资源。

  6. submit方法(三个重载方法)

    返回的Future对象可以用来取消任务和等待任务执行完成
  7. invokeAny和invokeAll方法

    用户批量执行任务,
    invokeAny:会阻塞当前线程,直到某个任务完成。并返回这个任务相关的Future对象
    invokeAll:会阻塞当前线程,直到所有任务完成。
  8. 两阶段shutdown

    1. 先执行shutdown方法
    2. 调用awaitTermination方法
    3. 再调用shutdownNow方法
    void shutdownAndAwaitTermination(ExecutorService pool) {
       pool.shutdown(); // Disable new tasks from being submitted
       try {
         // Wait a while for existing tasks to terminate
         if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
           pool.shutdownNow(); // Cancel currently executing tasks
           // Wait a while for tasks to respond to being cancelled
           if (!pool.awaitTermination(60, TimeUnit.SECONDS))
               System.err.println("Pool did not terminate");
         }
       } catch (InterruptedException ie) {
         // (Re-)Cancel if current thread also interrupted
         pool.shutdownNow();
         // Preserve interrupt status
         Thread.currentThread().interrupt();
       }
    }
  9. isShutdown和isTerminated分别对应于两个状态:关闭状态,终结状态

Thread

  1. interrupt方法
    如果执行a.interrupt方法后,如果a线程(注意是a线程,不是调用线程)抛出了InterruptedException异常,那么a的中断状态会被清除。如果不是抛出InterruptedException异常,那么a的中断状态都会被设置。
  2. interrupted方法
    执行a.interrupted方法会返回a线程的中断状态,同时会清除a线程的中断状态
  3. isInterrupted方法
    执行a.interrupted方法会返回a线程的中断状态,不会清除a线程的中断状态

ThreadPoolExecutor

  1. core pool size 及 max pool size

    一个新的任务提交哪些情况下回创建新的线程:
        1. 已创建的的线程数小于corePoolSize(即便有线程是空闲的)
        2. 已创建的线程数大于corePoolSize小于maxPoolSize,同时任务队列已经满的情况下,也会创建新的线程
    
    可以动态改变这两个的值:setCorePoolSize  以及 setMaximumPoolSize
    
    如果corePoolSize==maxinumPoolSize,那么则创建了一个固定大小的线程池
  2. keep alive time

    如果已创建的线程大于了corePoolSize,并且如果有线程的空闲时间大于了keepAliveTime,那么这些线程会被kill掉直到剩下corePoolSize个线程。
    
    可以动态设置:setKeepAliveTime方法
    
    默认情况下keep-alive策略只会针对已创建线程数大于corePoolSize的情况下
    
    可以通过执行allowCoreThreadTimeOut(boolean)让keep-alive策略应用在已创建线程数小于corePoolSize的情况下。
  3. BlockingQueue

    1. 如果已创建线程数小于corePoolSize,那么会创建新的线程来执行当前提交的任务,而不是进入阻塞队列
    2. 如果已创建线程数大于等于corePoolSize,会尝试先进入阻塞队列,如果进入失败(其实就是队列已满),则会在maxPoolSize条件下创建新的线程来执行当前提交的任务。如果不满足maxPoolSize条件,那么就会执行 拒绝执行策略(默认的拒绝执行策略见下)
    3. 通常有三种入队列策略

      1. 直接传递给线程(Direct handoffs)

        比如:SychronousQueue
        
        感觉可以理解为这个入队列会总是失败,就相当于没有这个队列一样。这样就能在maxPoolSize条件下尽可能快的创建(或选择空闲的线程)来执行新提交的任务。
        
        如果提交的任务有互相的依赖性,可以考虑使用这种队列。
        
        public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>());
        }
      2. 无界队列(Unbounded Queue)

        比如:LinkedBlockingQueue
        
        可以理解为如果有任务需要入队列,那么总会入队成功。
        因此按照创建新线程的条件,理论上不会有超过corePoolSize个数的线程。也就是说理论上线程数最多为corePoolSize,因此maxPoolSize的设置也就显得没有意义了。
        
        如果提交的任务互相间没有依赖性,可以考虑使用这种队列
      3. 有界队列(Bounded Queue)

        比如:ArrayBlockingQueue
        
        如果使用有限个maxPoolSize,那么使用这种队列可以防止资源的耗尽。
        
        使用长队列和小的线程池,可以降低CPU使用率,降低系统资源的消耗,以及降低线程上下文切换的消耗,但是会导致低吞吐量。如果任务频繁的阻塞,系统可能会创建比允许的线程数多的线程。
        
        使用短队列和大的线程池,可以提高CPU使用率,但也有可能导致吞吐量下降。
  4. 拒绝执行策略(我自己的叫法,实际上就是 RejectedExceptionHandler )

    这里写图片描述

    1. ThreadPoolExecutor.AbortPolicy
      抛出RejectedExecutionException异常

      public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
          throw new RejectedExecutionException("Task " + r.toString() +
                                               " rejected from " +
                                               e.toString());
      }
    2. ThreadPoolExecutor.CallerRunsPolicy
      在调用线程上执行(哪个线程提交的任务就哪个线程执行)

      public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
          if (!e.isShutdown()) {
              r.run();
          }
      }
  5. ThreadPoolExecutor.DiscardPolicy
    直接放弃

    ```
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
    ```
    
    1. ThreadPoolExecutor.DiscardOldestPolicy
      放弃当前队列中第一个任务

      public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
          if (!e.isShutdown()) {
              e.getQueue().poll();
              e.execute(r);
          }
      }
  6. Finalization

    一个在程序中不再被引用的线程池如果同时没有剩余的线程,那么这个线程池会被自动的shutdown.
    
    因此如果你想即便在忘记执行shutdown方法的时候仍能正常关闭线程池,那么建议设置一个有限的keepAliveTime(针对大于线程数大于corePoolSize的那些线程),同时也执行下 allowCoreThreadTimeOut(boolean) . 

欢迎关注公众号
70

目录
相关文章
|
3月前
|
算法 调度 芯片
RT-Thread快速入门-线程管理
RT-Thread快速入门-线程管理
38 0
RT-Thread快速入门-线程管理
|
3月前
|
数据处理 调度
RT-Thread快速入门-线程管理(下)
RT-Thread快速入门-线程管理(下)
22 0
|
9月前
|
Java
The Pitfalls of Using Executors to Create Thread Pools in Java
While the Executors class provides a convenient way to create thread pools in Java, it comes with inherent limitations that may not suit every use case. Blindly using Executors can lead to uncontrolled thread growth, task loss, and difficulties in handling exceptions. For robust and efficient concur
46 0
|
12月前
|
Java
ThreadPoolExecutor的中的submit和FutureTask || 通过Executors 创建线程池的一些实例(Callable和Runnable的在其中的体现)
ThreadPoolExecutor的中的submit和FutureTask || 通过Executors 创建线程池的一些实例(Callable和Runnable的在其中的体现)
116 0
|
Java API
【Java】什么是线程?Thread和Runnable区别
【Java】什么是线程?Thread和Runnable区别
86 0
【Java】什么是线程?Thread和Runnable区别
|
消息中间件 安全 Java
java 多线程 Thread & Runnable 使用与区别
1.线程和进程的定义 进程:是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。 线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位
260 0
|
数据采集 缓存 安全
Executors 与 Thread 比较
Executors 与 Thread 比较
75 0
|
Java
Java -多线程基础Thread和Runnable
打王者时与队友单独完成各自的事同时又能同时进行变是多线程
74 0
|
Java 调度 容器
Java中的Runnable、Callable、Future、FutureTask的区别与示例
Java中的Runnable、Callable、Future、FutureTask的区别与示例
167 0