複数のスレッドの作成、スレッドプールのワークフローなどを確認します。

序文

最近、プロジェクトをレビューして要約し、マルチスレッドを使用してプロジェクトに非同期タスクを実装する方法を共有しました。共有する前に、マルチスレッドの関連する知識ポイントを確認しましょう。

スレッドを作成する4つの方法

  • Threadクラスを継承し、runメソッドをオーバーライドします
  • Runnableインターフェイスを実装し、runメソッドを書き直します
  • Callableインターフェイスを実装し、FutureTaskと協力して戻り値を実装し、callメソッドを書き直します
  • スレッドプールのスケジューリングを通じて、スレッドプールを作成します。
    コードを見てみましょう。
import java.util.concurrent.*;
public class ThreadTest {
    
    

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        // System.out.println("main......start.....");

        // 1、通过继承Thread类
        // Thread thread = new Thread01();
        // thread.start();
        // System.out.println("main......end.....");

		// 2、通过实现Runnable接口
        // Runable01 runable01 = new Runable01();
        // new Thread(runable01).start();
		
		// 3、通过FutureTask传参Callable,实现有返回值
        // FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
        // new Thread(futureTask).start();
        // System.out.println(futureTask.get());

        // service.execute(new Runable01());

        System.out.println("main......start.....");
    }

    private static void threadPool() {
    
    

        ExecutorService threadPool = new ThreadPoolExecutor(
                200,
                10,
                10L,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(10000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }


    public static class Thread01 extends Thread {
    
    
        @Override
        public void run() {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
        }
    }


    public static class Runable01 implements Runnable {
    
    
        @Override
        public void run() {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
        }
    }


    public static class Callable01 implements Callable<Integer> {
    
    
        @Override
        public Integer call() throws Exception {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
            return i;
        }
    }
}

4つの方法でスレッド起動を作成することの違い

1番目と2番目のタイプは、タスクを実行するための戻り値のないスレッドを作成し、3番目のタイプは、タスクを実行するための戻り値のあるスレッドを作成します。
スレッドを作成する上記の3つの方法は、スレッドの作成と破棄に非常に時間がかかり、ビジネスの応答時間に影響を与えるため、ビジネスに表示できません。これらの他に、仮定すると、非常にリソースを消費します。現在、100,000の同時リクエストがあります。上記の3つの作成スレッドのいずれかを使用してタスクを実行する場合、理想的には100,000のスレッドを作成する必要があります。これは不可能です。期間中に間違いなくメモリが発生します。バーストし、サーバーがダウンしています。この場合、スレッドプールを使用してこの問題を解決できます
開発でスレッドプールが使用される理由を要約しましょう

  • リソース消費の削減:作成されたスレッドを再利用して、スレッドの作成と破棄によって引き起こされる損失を削減します
  • 応答速度の向上:スレッドプール内の一部のスレッドはアイドル状態です。タスクが発生すると、スレッドを作成せずにすぐに実行できます。
  • スレッドプールは、現在のシステムの特性に応じてプール内のスレッドを最適化し、スレッドの作成と破棄によって発生するシステムオーバーヘッドを削減します。スレッドを無制限に作成すると、システムリソースが破棄されるだけでなく、システムの安定性も低下し、すべてがスレッドプールに渡されます。統一された管理。

スレッドプールの知識ポイント

スレッドプールを作成するにはどうすればよいですか?
このコードで実行できます

ExecutorService threadPool = new ThreadPoolExecutor(
    200,
    10,
    10L,
    TimeUnit.SECONDS,
    new LinkedBlockingDeque<Runnable>(10000),
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy()
);

コンストラクターのパラメーターを見てみましょう(ソースコードの奥深く)

/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
    
    

corePoolSize:コアスレッドの数maximumPoolSize:スレッド
の最大数
keepAliveTime:タスクの実行がない場合、スレッドプール内のコアスレッドの数を超える最大存続時間。この時間を超えると、スレッドは破棄されます。
ユニット:時間単位
workQueue:ワークキュー、タスクの数が超過した場合コアスレッドの数は次のとおりです。アイドルスレッドがある限り、これらのタスクはワークキューに配置されます。これらのアイドルスレッドは、スレッドをフェッチするためにキューに移動します
。threadFactory:スレッド
ハンドラーを作成したファクトリ:スレッドプールが新しいタスクを受け入れることができなくなったときいつ、拒否戦略が採用されました

スレッドプールワークフロー
スレッドプールが作成され、送信されたタスクがコアスレッドの数より少ない場合、コアスレッドはすぐにタスクを実行し、数がコアスレッドの数を超えると、これらのタスクが実行されます。キューでは、ワークキューがいっぱいになると、タスクを実行するための非コアスレッドが作成されます。スレッドプール内のスレッド数が最大スレッド数に達すると、この時点でタスクが送信されると、拒否ポリシーがトリガーされてタスクが拒否されます。スレッドプール内のタスクが実行されると、アイドル状態のスレッドが存在します。アイドル時間がキープアライブ時間に達すると、それらの非コアスレッドは破棄され、コアスレッドはデフォルトでは破棄されません。jdkによって提供される
スレッドプール

// 固定线程数量的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
    
    
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
// 核心线程为0,但可以任意创建线程,最后都能被销毁
public static ExecutorService newCachedThreadPool() {
    
    
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
// 单个线程的线程池
public static ExecutorService newSingleThreadExecutor() {
    
    
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
// 执行定时任务的线程池,它可以指定多长时间以后执行任务
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    
    
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

拒否戦略
ここに写真の説明を挿入
最初の拒否戦略は次のとおりです。スレッドプールがタスクを受け入れることができなくなった場合、送信するタスクのrunメソッドを呼び出すことができますが、これは非同期ではなく同期的な方法です
。2番目の拒否戦略は次のとおりです。プールがタスクを受け入れることができなくなった場合、送信するタスクがある場合、プールはタスクの実行を直接拒否し、例外をスローします
.3番目の拒否戦略は、スレッドプールがタスクを受け入れることができなくなった場合、タスクが送信された場合、これです。タスクは削除されますが、例外はスローされません。4
番目の拒否戦略は、スレッドプールがタスクを受け入れることができなくなったとき、タスクが送信されたときに、キュー内で最も寿命の長いタスクを削除し、現在のタスクをワークキューに追加することです。と一緒に行きます

おすすめ

転載: blog.csdn.net/MarkusZhang/article/details/107871967