ホームページ  >  記事  >  Java  >  Java での ThreadPoolExecutor スレッド プールの使用法の概要

Java での ThreadPoolExecutor スレッド プールの使用法の概要

不言
不言転載
2019-04-04 09:55:363683ブラウズ

この記事では、Java での ThreadPoolExecutor スレッド プールの使用方法を紹介します。これには一定の参考値があります。必要な友人は参照してください。お役に立てば幸いです。

Executors

Executors は Java のツール クラスで、さまざまなタイプのスレッド プールを作成するためのファクトリ メソッドを提供します。

Java での ThreadPoolExecutor スレッド プールの使用法の概要

## 上の図から、スレッド プールを作成する Executors メソッドが ExecutorService インターフェイスを実装していることもわかります。一般的に使用されるメソッドは次のとおりです:

newFixedThreadPool(int Threads) :固定数のスレッドを使用してスレッド プールを作成します。過剰なスレッドはキューで待機します。

newCachedThreadPool(): キャッシュ可能なスレッド プールを作成します。スレッド プールの長さが処理の必要性を超える場合、アイドル状態のスレッドは柔軟にリサイクル (60 秒)。リサイクルがない場合は、新しいスレッドを作成します。

newSingleThreadExecutor(): シングルスレッド スレッド プールを作成します。これは、タスクの実行に唯一の作業スレッドのみを使用します。すべてのタスクは指定された順序 (FIFO、LIFO、優先順位) で実行されます。タスク実行エラーが発生した場合は、別のスレッドが実行を継続します。

newScheduledThreadPool(int corePoolSize): スケジュールされたスレッド プールと定期的なスレッド プールを作成します。

Executors Example


newCachedThreadPool

スレッドの最大数は Integer.MAX_VALUE です。スレッド プールのタスクに n を追加すると、これらの n 個のタスクはすべて一緒に実行されます。

        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        cachedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                for (;;) {
                    try {
                        Thread.currentThread().sleep(1000);
                        System.out.println(Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        cachedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                for (;;) {
                    try {
                        Thread.currentThread().sleep(1000);
                        System.out.println(Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
newFixedThreadPool

        ExecutorService cachedThreadPool = Executors.newFixedThreadPool(1);
        cachedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                for (;;) {
                    try {
                        Thread.currentThread().sleep(1000);
                        System.out.println(Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        cachedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                for (;;) {
                    try {
                        Thread.currentThread().sleep(1000);
                        System.out.println(Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

newScheduledThreadPool

は 3 秒に 1 回実行されます。 .

        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                for (;;) {
                    try {
                        Thread.currentThread().sleep(2000);
                        System.out.println(Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, 3, TimeUnit.SECONDS);
newSingleThreadExecutor

各タスクを順番に実行します。最初のタスクが実行されるまで、次のタスクは実行されません。

        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                for (;;) {
                    try {
                        System.out.println(Thread.currentThread().getName());
                        Thread.currentThread().sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                for (;;) {
                    try {
                        System.out.println(Thread.currentThread().getName());
                        Thread.currentThread().sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
とはExecutor に関する問題

Java での ThreadPoolExecutor スレッド プールの使用法の概要

Alibaba Java Development Manual には、Executor を使用してスレッド プールを作成すると OOM (OutOfMemory、メモリ オーバーフロー) が発生する可能性があると記載されていますが、そうではありません。その理由を説明しますので、最後に見てみましょう。なぜ執行者は許可されないのですか?

Executor の使用によって OOM が発生する状況をシミュレートする簡単な例から始めましょう。

/**
 * @author Hollis
 */
public class ExecutorsDemo {
    private static ExecutorService executor = Executors.newFixedThreadPool(15);
    public static void main(String[] args) {
        for (int i = 0; i  JVM パラメーター: -Xmx8m -Xms8m を指定することにより、上記のコードを実行すると OOM がスローされます: <p></p><pre class="brush:php;toolbar:false">Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371)
    at com.hollis.ExecutorsDemo.main(ExecutorsDemo.java:16)
上記のコードは、ExecutorsDemo.java の 16 行目がコード内の executor.execute(new SubThread()) であることを示しています。

Java には、BlockingQueue の主な実装が 2 つあり、それは ArrayBlockingQueue です。

ArrayBlockingQueue は配列で実装された境界付きブロッキング キューであり、容量を設定する必要があります。

public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <p>LinkedBlockingQueue はリンク リストで実装された境界付きブロッキング キューで、容量を設定できますオプションで、設定されていない場合は、最大長が Integer.MAX_VALUE の無制限のブロッキング キューになります。</p><pre class="brush:php;toolbar:false">public LinkedBlockingQueue() {
    this(Integer.MAX_VALUE);
}

ここでの問題は、LinkedBlockingQueue の容量を設定しない場合、デフォルトの容量が Integer になることです。 MAX_VALUE.

NewFixedThreadPool に LinkedBlockingQueue を作成する場合、容量は指定されません。このとき、LinkedBlockingQueue は無制限のキューです。無制限のキューの場合、キューにタスクを継続的に追加できます。この場合、タスクが多すぎるため、メモリ オーバーフローの問題が発生している可能性があります。

newCachedThreadPool および newScheduledThreadPool によって作成されるスレッドの最大数は Integer.MAX_VALUE である可能性があり、非常に多くのスレッドを作成する可能性があります。原因 OOM.

ThreadPoolExecutor はスレッド プールを作成します

Executor を使用してスレッド プールを作成しないでください (主にデフォルト実装の使用を避けるため)。その後、ThreadPoolExecutor のコンストラクターを直接呼び出してスレッド プールを自分で作成できます。 BlockQueue の容量を指定するだけです。

ExecutorService executor = new ThreadPoolExecutor(10, 10,
        60L, TimeUnit.SECONDS,
        new ArrayBlockingQueue(10));

この場合、送信されたスレッドの数が現在使用可能なスレッドの数を超えると、java.util.concurrent.RejectedExecutionException がスローされます。これは、現在使用されているキューが原因です。スレッド プールは制限されたキューです。キューがいっぱいになると、新しいリクエストの処理を続行できません。

ThreadPoolExecutor を自分で定義することに加えて、Apache や guava などの他のメソッドもあります。

4 つのコンストラクター

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

int corePoolSize => スレッド プール内のコア スレッドの最大数

スレッド プールが新しいスレッドを作成するとき、現在のスレッドの合計数が corePoolSize より小さい場合、新しいスレッド プールは新しいスレッドを作成します。これはコア スレッドです。corePoolSize を超えると、新しく作成された非コア スレッド


コア スレッドは、デフォルトでは、たとえコアスレッドは何もしません (アイドル状態)。

如果指定 ThreadPoolExecutor 的 allowCoreThreadTimeOut 这个属性为 true, 那么核心线程如果不干活(闲置状态)的话, 超过一定时间(时长下面参数决定), 就会被销毁掉

很好理解吧, 正常情况下你不干活我也养你, 因为我总有用到你的时候, 但有时候特殊情况(比如我自己都养不起了), 那你不干活我就要把你干掉了

int maximumPoolSize
该线程池中线程总数最大值

线程总数 = 核心线程数 + 非核心线程数.

long keepAliveTime
该线程池中非核心线程闲置超时时长

一个非核心线程, 如果不干活(闲置状态)的时长超过这个参数所设定的时长, 就会被销毁掉

如果设置 allowCoreThreadTimeOut = true, 则会作用于核心线程

TimeUnit unit

keepAliveTime的单位, TimeUnit是一个枚举类型, 其包括:

TimeUnit.DAYS;               //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒

BlockingQueue workQueue

一个阻塞队列, 用来存储等待执行的任务. 也就是说现在有10个任务, 核心线程 有四个, 非核心线程有六个, 那么这六个线程会被添加到 workQueue 中, 等待执行.

这个参数的选择也很重要, 会对线程池的运行过程产生重大影响, 一般来说, 这里的阻塞队列有以下几种选择:

SynchronousQueue: 这个队列接收到任务的时候, 会直接提交给线程处理, 而不保留它, 如果所有线程都在工作怎么办? 那就*新建一个线程来处理这个任务!所以为了保证不出现的错误, 使用这个类型队列的时候, maximumPoolSize 一般指定成 Integer.MAX_VALUE, 即无限大.

LinkedBlockingQueue: 这个队列接收到任务的时候, 如果当前线程数小于核心线程数, 则核心线程处理任务; 如果当前线程数等于核心线程数, 则进入队列等待. 由于这个队列最大值为 Integer.MAX_VALUE , 即所有超过核心线程数的任务都将被添加到队列中,这也就导致了 maximumPoolSize 的设定失效, 因为总线程数永远不会超过 corePoolSize.

ArrayBlockingQueue: 可以限定队列的长度, 接收到任务的时候, 如果没有达到 corePoolSize 的值, 则核心线程执行任务, 如果达到了, 则入队等候, 如果队列已满, 则新建线程(非核心线程)执行任务, 又如果总线程数到了maximumPoolSize, 并且队列也满了, 则发生错误.

DelayQueue: 队列内元素必须实现 Delayed 接口, 这就意味着你传进去的任务必须先实现Delayed接口. 这个队列接收到任务时, 首先先入队, 只有达到了指定的延时时间, 才会执行任务.

ThreadFactory threadFactory

它是ThreadFactory类型的变量, 用来创建新线程.

默认使用 Executors.defaultThreadFactory() 来创建线程. 使用默认的 ThreadFactory 来创建线程时, 会使新创建的线程具有相同的 NORM_PRIORITY 优先级并且是非守护线程, 同时也设置了线程的名称.

RejectedExecutionHandler handler

表示当拒绝处理任务时的策略, 有以下四种取值:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常(默认).
ThreadPoolExecutor.DiscardPolicy:直接丢弃任务, 但是不抛出异常.
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务, 然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:用调用者所在的线程来执行任务.

【相关推荐:Java视频教程

以上がJava での ThreadPoolExecutor スレッド プールの使用法の概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。