複数の角度はあなたのJavaスレッドプールの深い理解の完全な範囲を持って来ます

I.はじめに

スレッドプールとは何ですか

スレッドプールは、タスクを処理するマルチスレッド処理のフォームをキューに追加して、スレッドが作成されたときに自動的にこれらの作業を開始されますです。

なぜ、スレッドプールを使用します

同時要求の数多くが、各スレッドの実行時間が非常に短い場合は、頻繁に作成および破棄のスレッドが表示されます。このように、それは非常に頻繁に時間のスレッドを作成し、破壊することができる、システムの効率を低下させる、リソースのオーバーヘッドは、実際の仕事よりも大きいことが必要。

スレッドプールを導入する必要があり、この問題のためです。使用する  スレッドプールの利点を  、以下の通りであります:

  • リソースの消費を削減  -作成されたスレッドを再利用することで、消費に起因するスレッドの作成と破壊を減らします。
  • 応答時間を改善  -タスクが到着したときに、タスクは、スレッドの作成を直ちに実施することが可能になるまで待つ必要がないかもしれません。
  • スレッドの管理性を向上させる  無制限の作成は、システムリソースを消費していない場合にのみ、スレッドは、希少資源であるが、また、流通、チューニングおよび監視を統一することができますスレッドプールを使用して、システムの安定性を減らします- 。しかし、スレッドプールの合理的な使用することは、その原則をよく知っている必要があります。

二、エグゼキュータフレームワーク

エグゼキュータフレームワークは、非同期タスク実行戦略呼び出し、スケジューリング、実行およびコントロールのセットに基づいてフレームワークであり、その目的は、「ジョブの送信」メカニズムと「どのようにタスクを実行する」分離のを提供することです。

コアAPIの概要

エグゼキュータのフレームワークのコアAPI次のように:

  • Executor - タスクを実行するためのシンプルなインターフェース。
  • ExecutorService -延び  Executor インタフェースを。スケーラビリティ:
    • 支持スレッド戻り値。
    • ライフサイクル管理スレッドをサポートしています。
  • ScheduledExecutorService -延び  ExecutorService インタフェースを。スケーラビリティ:定期的なタスクのサポート。
  • AbstractExecutorService -  ExecutorService インタフェースのデフォルトの実装。
  • ThreadPoolExecutor -継承コアクラスの執行フレームワーク  AbstractExecutorService クラス。
  • ScheduledThreadPoolExecutor -  ScheduledExecutorService インタフェース、時限スケジュールスレッドプールのタスクの実装。
  • Executors -呼び出すことにより、  Executors スレッドプールの静的ファクトリメソッドとリターン作成する  ExecutorService オブジェクトを。

複数の角度はあなたのJavaスレッドプールの深い理解の完全な範囲を持って来ます

エグゼキュータ

Executor インターフェイスのみ定義  execute 受信するための方法  Runnable のオブジェクトを。

public interface Executor {
    void execute(Runnable command);
}

ExecutorServiceの

ExecutorService インタフェースは延び  Executor インターフェイス、それが提供する  invokeAllinvokeAnyshutdownsubmit および他の方法を。

public interface ExecutorService extends Executor {

    void shutdown();

    List<Runnable> shutdownNow();

    boolean isShutdown();

    boolean isTerminated();

    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;

    <T> Future<T> submit(Callable<T> task);

    <T> Future<T> submit(Runnable task, T result);

    Future<?> submit(Runnable task);

    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;

    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;

    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;

    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

比べ:見やすいその支援に規定された方法  Executor 、インターフェースExecutorService 主拡張インターフェースです。

  • 支持スレッドの値を返す  sumbit- invokeAllinvokeAny 方法は、着信サポートされているCallable オブジェクト。
  • サポートライフサイクル管理スレッド  shutdown- shutdownNowisShutdown および他の方法。

ScheduledExecutorService

ScheduledExecutorService インターフェイスは、拡張  ExecutorService インターフェイスを。

前の二つのインターフェースの全てをサポートするその能力に加えて、それはまた、タイミングスケジュールスレッドをサポートします。

public interface ScheduledExecutorService extends ExecutorService {

    public ScheduledFuture<?> schedule(Runnable command,
                                       long delay, TimeUnit unit);

    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay, TimeUnit unit);

    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);

    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);

}

その拡張インタフェースには、次の機能を提供します。

  • schedule この方法は、指定された遅延の後に行うことができる  Runnable 、または  Callable タスク。
  • scheduleAtFixedRate 方法および  scheduleWithFixedDelay 方法は、通常のタスクを実行する時間間隔を指定しました。

三、ThreadPoolExecutor

java.uitl.concurrent.ThreadPoolExecutor クラスは、  Executor コアクラスでフレームワーク。そこで、本論文では、どのようなこのクラスに焦点を当てています。

重要な分野

ThreadPoolExecutor これは、次の重要なフィールドがあります。

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

パラメータ:

  • ctl -  動作状態とスレッドプールスレッドプールを制御するために使用されるアクティブなスレッドの数これは、2つの部分で構成されています。
    • スレッドプールの動作状態(runState
    • 有効なスレッドプール内のスレッド数(workerCount
    • それは見ることができますctl 使用して  Integer 3つの高保存、保存するタイプを  runState29低保存しました、  workerCountCOUNT_BITS 、29でCAPACITY あるマイナス1(29 1)29 1を左に、この定数は表し  workerCount 上限は5億についてです。
  • 動作状態 - スレッドが5つの動作状態の合計をプール:
    • RUNNING -  動作ステータス新しいタスクを受け入れるには、また、ブロッキングキューのタスクを処理することができます。
    • SHUTDOWN -  オフ私たちは、新しいタスクを受け入れませんが、ブロッキングキューのタスクを処理することができます。
      • でスレッドプールでは  RUNNING 状態、呼び出した  shutdown メソッドは、スレッドプールは、状態に入るようになります。
      • finalize この方法はまた、実行中に呼び出される  shutdown 状態を入力する方法。
    • STOP -  停止状態新しいタスクを受け入れてはいけない、それはタスクキューを処理しません。中断されたスレッドがタスクを処理しています。スレッドプールではでている  RUNNING か  SHUTDOWN の状態は、呼び出す  shutdownNow メソッドが状態を入力するスレッドプールを作成します。
    • TIDYING -  仕上げ状態すべてのタスクが終了している場合は、workerCount (スレッドの有効数)が0になった後、状態を入力するには、スレッドプールは、呼び出しを  terminated 入力する方法  TERMINATED の状態を。
    • TERMINATED -  終了状態。 terminated 方法状態に入った後に行います。デフォルトの  terminated メソッドは何もしません。入力し  TERMINATED 、以下の条件を:
      • スレッドプールではありません  RUNNING 状態。
      • スレッドプールの状態ではありません  TIDYING 状態または  TERMINATED 状態;
      • 状態は、スレッドプールの場合  SHUTDOWN と  workerQueue 空。
      • workerCount 0;
      • セットアップ  TIDYING に成功状態。

複数の角度はあなたのJavaスレッドプールの深い理解の完全な範囲を持って来ます

コンストラクタ

ThreadPoolExecutor 最初の3がすべて第四実装に基づいてされている4つの構成方法があります。第四の方法は、以下の構造です。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {

パラメータ:

  • corePoolSize -  カーネルスレッドの数新しいタスクとき  execute の方法を提出する際には、スレッドプールには、以下の判定を行います。
    • 実行中のスレッドの数より少ない場合は  corePoolSize、プール内の他のスレッドがアイドル状態であっても、タスクを処理するための新しいスレッドを作成します。
    • プールまたはそれ以上でのスレッドの数の場合  corePoolSize と少なく  maximumPoolSize、場合にのみ  workQueue タスクを処理するための新しいスレッドを作成する際に、完全な。
    • セットした場合  corePoolSize と  maximumPoolSize 同じサイズは、スレッドプール内に作成されて固定されています。新しいジョブの投入がある場合ならば、  workQueue 完全な、要求が置かれますない  workQueue で、から待つ空きスレッドがある  workQueue タスクとプロセスを取るには、
    • スレッドの数がより多いを実行しているか、または等しい場合は  maximumPoolSize、その場合には  workQueue 、完全な、そして使用し  handler たタスクに対処するために、指定されたポリシーを、
    • したがって、タスクはそれが順に判定され、送信され  corePoolSize =>  workQueue => maximumPoolSize
  • maximumPoolSize -  スレッドの最大数
    • キューがいっぱいになると、少ないスレッドの最大数よりも作成されているスレッドの数と、スレッド・プールには、タスクを実行するために、新しいスレッドを再作成します。
    • 值得注意的是:如果使用了***的任务队列这个参数就没什么效果。
  • keepAliveTime线程保持活动的时间
    • 当线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime
    • 所以,如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
  • unit - keepAliveTime 的时间单位。有 7 种取值。可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。
  • workQueue - 等待执行的任务队列。用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列。
    • ArrayBlockingQueue - 有界阻塞队列
      • 此队列是基于数组的先进先出队列(FIFO)
      • 此队列创建时必须指定大小。
    • LinkedBlockingQueue - ***阻塞队列
      • 此队列是基于链表的先进先出队列(FIFO)
      • 如果创建时没有指定此队列大小,则默认为 Integer.MAX_VALUE
      • 吞吐量通常要高于 ArrayBlockingQueue
      • 使用 LinkedBlockingQueue 意味着: maximumPoolSize 将不起作用,线程池能创建的最大线程数为 corePoolSize,因为任务等待队列是***队列。
      • Executors.newFixedThreadPool 使用了这个队列。
    • SynchronousQueue - 不会保存提交的任务,而是将直接新建一个线程来执行新来的任务
      • 每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。
      • 吞吐量通常要高于 LinkedBlockingQueue
      • Executors.newCachedThreadPool 使用了这个队列。
    • PriorityBlockingQueue - 具有优先级的***阻塞队列
  • threadFactory - 线程工厂。可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
  • handler - 饱和策略。它是 RejectedExecutionHandler 类型的变量。当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。线程池支持以下策略:
    • AbortPolicy - 丢弃任务并抛出异常。这也是默认策略。
    • DiscardPolicy - 丢弃任务,但不抛出异常。
    • DiscardOldestPolicy - 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)。
    • CallerRunsPolicy - 只用调用者所在的线程来运行任务。
    • 如果以上策略都不能满足需要,也可以通过实现 RejectedExecutionHandler 接口来定制处理策略。如记录日志或持久化不能处理的任务。

execute 方法

默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程。

提交任务可以使用 execute 方法,它是 ThreadPoolExecutor 的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行

execute 方法工作流程如下:

  1. 如果 workerCount &lt; corePoolSize,则创建并启动一个线程来执行新提交的任务;
  2. 如果 workerCount &gt;= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中;
  3. 如果 workerCount &gt;= corePoolSize && workerCount &lt; maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务;
  4. 如果workerCount &gt;= maximumPoolSize,并且线程池内的阻塞队列已满,则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。

複数の角度はあなたのJavaスレッドプールの深い理解の完全な範囲を持って来ます

其他重要方法

在 ThreadPoolExecutor 类中还有一些重要的方法:

  • submit - 类似于 execute,但是针对的是有返回值的线程。submit 方法是在 ExecutorService 中声明的方法,在 AbstractExecutorService 就已经有了具体的实现。ThreadPoolExecutor 直接复用 AbstractExecutorService 的 submit 方法。
  • shutdown - 不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务。
    • 将线程池切换到 SHUTDOWN 状态;
    • 并调用 interruptIdleWorkers 方法请求中断所有空闲的 worker;
    • 最后调用 tryTerminate 尝试结束线程池。
  • shutdownNow - 立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。与 shutdown 方法类似,不同的地方在于:
    • 设置状态为 STOP
    • 中断所有工作线程,无论是否是空闲的;
    • 取出阻塞队列中没有被执行的任务并返回。
  • isShutdown - 调用了 shutdown 或 shutdownNow 方法后,isShutdown 方法就会返回 true。
  • isTerminaed - 当所有的任务都已关闭后,才表示线程池关闭成功,这时调用 isTerminaed 方法会返回 true。
  • setCorePoolSize - 设置核心线程数大小。
  • setMaximumPoolSize - 设置最大线程数大小。
  • getTaskCount - 线程池已经执行的和未执行的任务总数;
  • getCompletedTaskCount - 线程池已完成的任务数量,该值小于等于 taskCount
  • getLargestPoolSize - 线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过,也就是达到了 maximumPoolSize
  • getPoolSize - 线程池当前的线程数量;
  • getActiveCount - 当前线程池中正在执行任务的线程数量。

使用示例

public class ThreadPoolExecutorDemo {

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 500, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

        for (int i = 0; i < 100; i++) {
            threadPoolExecutor.execute(new MyThread());
            String info = String.format("线程池中线程数目:%s,队列中等待执行的任务数目:%s,已执行玩别的任务数目:%s",
                threadPoolExecutor.getPoolSize(),
                threadPoolExecutor.getQueue().size(),
                threadPoolExecutor.getCompletedTaskCount());
            System.out.println(info);
        }
        threadPoolExecutor.shutdown();
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " 执行");
        }

    }

}

四、Executors

JDK 的 Executors 类中提供了几种具有代表性的线程池,这些线程池 都是基于 ThreadPoolExecutor 的定制化实现

在实际使用线程池的场景中,我们往往不是直接使用 ThreadPoolExecutor ,而是使用 JDK 中提供的具有代表性的线程池实例。

newSingleThreadExecutor

创建一个单线程的线程池

只会创建唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它 。

单工作线程最大的特点是:可保证顺序地执行各个任务

示例:

public class SingleThreadExecutorDemo {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 执行");
                }
            });
        }
        executorService.shutdown();
    }

}

newFixedThreadPool

创建一个固定大小的线程池

每次提交一个任务就会新创建一个工作线程,如果工作线程数量达到线程池最大线程数,则将提交的任务存入到阻塞队列中

FixedThreadPool 是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

示例:

public class FixedThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 执行");
                }
            });
        }
        executorService.shutdown();
    }

}

newCachedThreadPool

创建一个可缓存的线程池

  • スレッドプール内のスレッドの数は、処理タスクが必要以上に長い場合、アイドル状態のスレッドの一部が回復します。
  • 長い時間は、スレッドプールにタスクを提出しなかった場合は、ワーカースレッドが指定された時間のためにアイドル状態になっている場合、それは、ある(デフォルトは1分です)、その後、ワーカースレッドは自動的に終了します。終了した場合、しかし、あなたが再作成ワーカースレッドに新しいタスク、スレッドプールを提出した場合。
  • このスレッドプールは、スレッドプールのサイズを制限しない、スレッドプールのスレッドの最大サイズは、作成することができるオペレーティングシステム(またはJVM)のサイズに完全に依存します。そのため、使用は  CachedThreadPool、原因同時に実行するスレッドの数が多い、大きな原因のシステムダウンに、そうでない場合、制御タスクの数に注意を払うようにしてください。

例:

public class CachedThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 执行");
                }
            });
        }
        executorService.shutdown();
    }

}

newScheduleThreadPool

スレッドプールのサイズ制限を作成します。このスレッドプールは、タイミングや、定期的にタスクを実行する必要性をサポートします。

public class ScheduledThreadPoolDemo {

    public static void main(String[] args) {
        schedule();
        scheduleAtFixedRate();
    }

    private static void schedule() {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 100; i++) {
            executorService.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 执行");
                }
            }, 1, TimeUnit.SECONDS);
        }
        executorService.shutdown();
    }

    private static void scheduleAtFixedRate() {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 100; i++) {
            executorService.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 执行");
                }
            }, 1, 1, TimeUnit.SECONDS);
        }
        executorService.shutdown();
    }

}

おすすめ

転載: blog.51cto.com/14230003/2462577