番外1、多线程

Executor

Module java.base
Package java.util.concurrent
Interface Executor

  执行已提交的可运行任务的对象。该接口提供了一种将任务提交与如何运行每个任务的机制(包括线程使用、调度等细节)分离的方法。通常使用Executor而不是显式地创建线程。
  例如,相比一组任务中的每个任务调用new Thread(new RunnableTask()).start() ,采用如下的方法更好:

 Executor executor = anExecutor();
 executor.execute(new RunnableTask1());
 executor.execute(new RunnableTask2());
 ...

  但是,Executor接口并不严格要求执行是异步的。在最简单的情况下,executor可以立即在调用者的线程中运行提交的任务:

class DirectExecutor implements Executor {
   public void execute(Runnable r) {
     r.run();
   }
 }

  更典型的情况是,任务在调用者的线程之外的某个线程中执行。下面的执行程序为每个任务生成一个新线程。

class ThreadPerTaskExecutor implements Executor {
   public void execute(Runnable r) {
     new Thread(r).start();
   }
 }

  许多Executor实现对如何以及何时调度任务施加了某种限制。下面的执行程序将任务的提交序列化到第二个执行程序,演示了复合执行程序。

class SerialExecutor implements Executor {
   final Queue<Runnable> tasks = new ArrayDeque<>();
   final Executor executor;
   Runnable active;

   SerialExecutor(Executor executor) {
     this.executor = executor;
   }

   public synchronized void execute(Runnable r) {
     tasks.add(() -> {
       try {
         r.run();
       } finally {
         scheduleNext();
       }
     });
     if (active == null) {
       scheduleNext();
     }
   }

   protected synchronized void scheduleNext() {
     if ((active = tasks.poll()) != null) {
       executor.execute(active);
     }
   }
 }

此包中提供的Executor实现了ExecutorService,这是一个用处更加广泛的接口。

ThreadPoolExecutor类提供可扩展的线程池实现。
Executors 类为这些executor提供了方便的工厂方法。

ExecutorService

public interface ExecutorService extends Executor

  ExecutorService是一个Executor, 它提供用于管理终止,以及可以产生用于跟踪一个或多个异步任务进展的Future的方法。ExecutorService可以被关闭,这将导致它拒绝新任务。提供了两种不同的方法来关闭ExecutorService。shutdown()方法允许在终止之前执行以前提交的任务,而shutdownNow()方法可以防止等待的任务启动并尝试停止当前正在执行的任务。在终止时,执行程序没有正在执行的任务,没有等待执行的任务,也不能提交新的任务。

  方法submit扩展了基本方法Executor.execute(Runnable),创建并返回一个可用于取消执行和/或等待完成的Future。
  方法invokeAny和invokeAll执行最常用的批量执行形式,执行一组任务,然后等待至少一个或全部任务完成。(类ExecutorCompletionService可用于编写这些方法的定制变体。)

Executors 类为这个包中提供的executor服务提供工厂方法。

使用案例:
下面是一个网络服务的例子,其中线程池服务中的线程传入请求。它使用预配置的Executors.newFixedThreadPool(int)工厂方法:

class NetworkService implements Runnable {
   private final ServerSocket serverSocket;
   private final ExecutorService pool;

   public NetworkService(int port, int poolSize)
       throws IOException {
     serverSocket = new ServerSocket(port);
     pool = Executors.newFixedThreadPool(poolSize);
   }

   public void run() { // run the service
     try {
       for (;;) {
         pool.execute(new Handler(serverSocket.accept()));
       }
     } catch (IOException ex) {
       pool.shutdown();
     }
   }
 }

下面的方法分两个阶段关闭ExecutorService,首先调用shutdown来拒绝传入的任务,然后在必要时调用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();
   }
 }

AbstractExecutorService

public abstract class AbstractExecutorService
extends Object
implements ExecutorService

  提供ExecutorService执行方法的默认实现。这个类使用newTaskFor返回的RunnableFuture实现了submit、invokeAny和invokeAll方法,这个RunnableFuture默认是这个包中提供的FutureTask类。例如,submit(Runnable)的实现创建了一个执行并返回的关联的RunnableFuture。子类可以覆盖newTaskFor方法来返回RunnableFuture实现,而不是FutureTask。
  下面是一个类的示意图,它定制ThreadPoolExecutor来使用CustomTask类,而不是默认的FutureTask:

public class CustomThreadPoolExecutor extends ThreadPoolExecutor {

   static class CustomTask<V> implements RunnableFuture<V> {...}

   protected <V> RunnableFuture<V> newTaskFor(Callable<V> c) {
       return new CustomTask<V>(c);
   }
   protected <V> RunnableFuture<V> newTaskFor(Runnable r, V v) {
       return new CustomTask<V>(r, v);
   }
   // ... add constructors, etc.
 }

ThreadPoolExecutor

public class ThreadPoolExecutor extends AbstractExecutorService

  使用可能的几个池线程之一执行每个提交的任务的ExecutorService,通常使用exec工厂方法进行配置。

  线程池解决了两个不同的问题:由于减少了每个任务的调用开销,线程池通常在执行大量异步任务时提供了更好的性能,并且提供了一种绑定和管理资源(包括执行任务集合时消耗的线程)的方法。

  为了在广泛的上下文中有用,这个类提供了许多可调参数和可扩展性挂钩。然而,程序员要求使用更方便的Executors 工厂方法Executors.newCachedThreadPool()(无界的线程池,线程自动回收),Executors.newFixedThreadPool (int)(固定大小的线程池)和Executors.newSingleThreadExecutor()(单个后台线程),preconfigure设置为最常见的使用场景。否则,在手动配置和调优该类时,请使用以下指南:

  1. Core and maximum pool sizes。
       ThreadPoolExecutor将根据corePoolSize(参见getCorePoolSize())和maximumPoolSize(参见getMaximumPoolSize())设置的界限自动调整池大小(参见getPoolSize())。在方法execute(Runnable)中提交新任务时,如果运行的线程小于corePoolSize,则创建一个新线程来处理请求,即使其他工作线程处于空闲状态。
       否则,如果运行的线程小于maximumPoolSize,则只在队列满时创建一个新线程来处理请求。
       通过将corePoolSize和maximumPoolSize设置为相同的值,您可以创建一个固定大小的线程池。通过将maximumPoolSize设置为一个基本无界的值,如Integer.MAX_VALUE,允许池容纳任意数量的并发任务。最典型的情况是,仅在构建时设置核心和最大池大小,但也可以使用setCorePoolSize(int)和setMaximumPoolSize(int)动态更改它们。

  2. 按需构建
      默认情况下,即使是核心线程也只是在新任务到达时才创建和启动,但是可以使用方法prestartCoreThread()或prestartAllCoreThreads()动态地覆盖它。如果使用非空队列构造池,则可能需要预启动线程。

  3. 创建新线程
      使用ThreadFactory创建新线程。如果没有另外指定,将使用executols . defaultthreadfactory(),它将创建所有属于相同ThreadGroup的线程,并且具有相同的NORM_PRIORITY优先级和非守护进程状态。通过提供不同的ThreadFactory,您可以更改线程的名称、线程组、优先级、守护进程状态等。如果ThreadFactory通过从newThread返回null来创建一个线程失败,执行程序将继续执行,但可能无法执行任何任务。线程应该拥有“modifyThread”RuntimePermission。如果工作线程或使用池的其他线程不拥有此权限,服务可能会降级:配置更改可能不会及时生效,关闭池可能仍然处于可能终止但尚未完成的状态。

  4. 保持存活时间
      如果当前池中有超过corePoolSize的线程,那么如果空闲时间超过keepAliveTime(请参阅getKeepAliveTime(TimeUnit)),则多余的线程将被终止。这提供了一种在池没有被积极使用时减少资源消耗的方法。如果以后池变得更活跃,就会构造新的线程。还可以使用setKeepAliveTime(long, TimeUnit)方法动态更改此参数。使用Long.MAX_VALUE TimeUnit.NANOSECONDS有效地禁止空闲线程在关闭之前终止。默认情况下,keep-alive策略只适用于拥有多于corePoolSize线程的情况,但是也可以使用allowCoreThreadTimeOut(boolean)方法将这个超时策略应用于核心线程,只要keepAliveTime值不为零。

  5. 队列
    任何BlockingQueue都可以用来传输和保存提交的任务。此队列的使用与池大小调整相互作用。

    • 如果运行的线程小于corePoolSize,则执行程序总是希望添加新线程而不是排队
    • 如果正在运行的线程数目大于等于corePoolSize,执行程序总是希望对请求进行排队,而不是添加新线程。
    • 如果一个请求不能排队,那么将创建一个新线程,除非这个线程的大小超过maximumPoolSize,在这种情况下,任务将被拒绝。

    排队有三种基本策略:

    • 直接的传递。工作队列的一个很好的默认选择是SynchronousQueue,它将任务传递给线程,而不会占用线程。在这里,如果没有立即可用的线程来运行任务,则对任务进行排队的尝试将失败,因此将构造一个新线程。此策略在处理可能具有内部依赖项的请求集时避免锁定。直接移交通常需要无界的最大池大小,以避免拒绝新提交的任务。反过来,当命令到达的平均速度比它们被处理的速度还要快时,就有可能出现无限的线程增长。
    • 无界队列。当所有的corePoolSize线程都处于繁忙状态时,使用无界队列(例如没有预定义容量的LinkedBlockingQueue)将导致新任务在队列中等待。因此,创建的线程不会超过corePoolSize。(因此,maximumPoolSize的值没有任何影响。)当每个任务完全独立于其他任务时,这可能是合适的,因此任务不会影响其他任务的执行;例如,在web页面服务器中。虽然这种类型的队列在平滑短暂的请求突发方面很有用,但它也承认,当命令平均到达速度超过处理速度时,可能会出现无限的工作队列增长。
    • 有界的队列。有限的队列(例如,ArrayBlockingQueue)在使用有限的最大池大小时有助于防止资源耗尽,但是调优和控制可能更困难。队列大小和最大池大小可以相互交换:使用大队列和小池可以最小化CPU使用、操作系统资源和上下文切换开销,但是会导致人为的低吞吐量。如果任务经常阻塞(例如,它们受到I/O的限制),系统可能会为比其他方式允许的更多的线程安排时间。使用小队列通常需要更大的池大小,这会使cpu更忙,但可能会遇到无法接受的调度开销,这也会降低吞吐量。
  6. 拒绝接受任务
    在方法execute(Runnable)中提交的新任务将在执行程序关闭时被拒绝,并且在执行程序对最大线程和工作队列容量使用有限的界限并达到饱和时也会被拒绝。在这两种情况下,execute方法都会调用RejectedExecutionHandler.rejectedExecution(Runnable, ThreadPoolExecutor)方法的RejectedExecutionHandler。提供四个预定义的处理程序策略:

    • 在默认的ThreadPoolExecutor.AbortPolicy中。处理程序在拒绝时抛出运行时RejectedExecutionException异常。
    • 在 ThreadPoolExecutor.CallerRunsPolicy中。调用execute本身的线程将运行该任务。这提供了一个简单的反馈控制机制,可以降低新任务的提交速度。
    • 在ThreadPoolExecutor.DiscardPolicy中。无法执行的任务将被丢弃。
    • 在ThreadPoolExecutor.DiscardOldestPolicy中,如果执行器没有关闭,工作队列头部的任务将被删除,然后重新执行(可能再次失败,导致重复执行)。
      可以定义和使用其他类型的RejectedExecutionHandler类。这样做需要谨慎,特别是当策略被设计为仅在特定容量或队列策略下工作时。
  7. Hook methods
      该类提供protected的可覆盖的beforeExecute(Thread, Runnable) 和afterExecute(Runnable, Throwable) 方法,这些方法在每个任务执行之前和之后调用。这些可以用来操作执行环境;例如,重新初始化threadlocal、收集统计信息或添加日志条目。此外,可以覆盖terminate()方法,以便在执行程序完全终止后执行需要执行的任何特殊处理。
      如果hook, callback, or BlockingQueue方法抛出异常,则内部工作线程可能会失败、突然终止并可能被替换。

  8. 队列的维护
    方法getQueue()允许访问工作队列,以便进行监视和调试。强烈反对将此方法用于任何其他目的。提供的两个方法remove(Runnable)和purge()可用于在大量排队的任务被取消时帮助回收存储。

  9. 回收
    不再在程序中引用并且没有剩余线程的池可以被回收(垃圾回收),而不需要显式关闭。您可以配置一个池,通过设置适当的keep-alive时间(使用0个核心线程的下限)和/或设置allowCoreThreadTimeOut(布尔值)来允许所有未使用的线程最终死亡。

扩展的例子。该类的大多数扩展都会覆盖一个或多个受保护的钩子方法。例如,这里有一个子类,添加了一个简单的暂停/恢复功能:

class PausableThreadPoolExecutor extends ThreadPoolExecutor {
   private boolean isPaused;
   private ReentrantLock pauseLock = new ReentrantLock();
   private Condition unpaused = pauseLock.newCondition();

   public PausableThreadPoolExecutor(...) { super(...); }

   protected void beforeExecute(Thread t, Runnable r) {
     super.beforeExecute(t, r);
     pauseLock.lock();
     try {
       while (isPaused) unpaused.await();
     } catch (InterruptedException ie) {
       t.interrupt();
     } finally {
       pauseLock.unlock();
     }
   }

   public void pause() {
     pauseLock.lock();
     try {
       isPaused = true;
     } finally {
       pauseLock.unlock();
     }
   }

   public void resume() {
     pauseLock.lock();
     try {
       isPaused = false;
       unpaused.signalAll();
     } finally {
       pauseLock.unlock();
     }
   }
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章