スレッドプールの件


1.スレッドプールとは
スレッドプールはマルチスレッド処理技術です.スレッドプールは、まずいくつかのスレッドを作成し、これらのスレッドを管理します.新しいタスクが来ると、作成されたアイドルスレッドにタスクを追加して実行します.スレッドプールで作成されるスレッド優先度は同じなので、特定のスレッド優先度を使用するタスクではスレッドプールを使用しないでください.
2.スレッドプールの利点と応用
  • スレッドプールがスレッドを統一的に管理することで、スレッドの頻繁な作成と破棄のシステムスケジューリングのオーバーヘッドが減少し、サーバが同時タスクを処理するパフォーマンスが大幅に向上します.
  • スレッドプールは、HTTP要求を処理するなど、頻繁なタスクスケジューリングに適しており、タスクが多く、タスクサイクルが小さい
  • である.
    3.スレッドプールが必要な理由
    現在のネットワークサーバの多くは、Webサーバ、Emailサーバ、データベースサーバなど、単位時間当たりに膨大な数の接続要求を処理しなければならないが、処理時間は比較的短いという共通点がある.
    従来のマルチスレッドスキームで採用されているサーバモデルは、リクエストを受信すると、新しいスレッドを作成し、そのスレッドによってタスクを実行します.タスクの実行が完了すると、スレッドは終了します.これが「即時作成、即時破棄」のポリシーです.スレッドの作成時間は、作成プロセスに比べて大幅に短縮されますが、スレッドにコミットされたタスクが実行時間が短く、実行回数が極めて頻繁である場合、サーバはスレッドの作成を停止せず、スレッドを破棄します.
    従来のスキームにおけるスレッド実行プロセスを,T 1,T 2,T 3の3つのプロセスに分けた.
  • T 1:スレッド作成時間
  • T 2:スレッドの同期等を含むスレッド実行時間
  • T 3:スレッド破棄時間
  • スレッド自体のオーバーヘッドの割合は(T 1+T 3)/(T 1+T 2+T 3)であることが分かる.スレッドの実行時間が短い場合、これはオーバーヘッドより20%-50%程度を占める可能性があります.タスクの実行時間が頻繁であれば、このオーバーヘッドは無視できません.
    これに加えて、スレッドプールは作成されたスレッドの数を減らすことができます.通常、スレッドプールで許可されるコンカレント・スレッドには上界があり、同時にコンカレントする必要があるスレッドの数が上界を超えると、一部のスレッドが待機します.従来のスキームでは、同時要求数が2000である場合、最悪の場合、システムは2000個のスレッドを生成する必要がある場合がある.これは大きな数ではありませんが、一部の機械ではこのような要求に達しない可能性があります.
    したがって,スレッドプールの出現は,スレッドプール自体によるオーバーヘッドの低減に着目している.スレッドプールは、事前に作成された技術を採用しており、アプリケーションが起動すると、すぐに一定数のスレッド(N 1)が作成され、空きキューに格納されます.これらのスレッドは、CPUを消費することなく、ブロックされた状態にあるが、メモリ容量は小さい.タスクが到来すると、バッファプールは空きスレッドを選択し、タスクをこのスレッドに転送して実行します.N 1スレッドがすべてタスクを処理した後、バッファプールは自動的に一定数の新しいスレッドを作成し、より多くのタスクを処理する.タスクの実行が完了した後もスレッドは終了せず、次のタスクをプールで待機し続けます.システムが比較的空いている場合、ほとんどのスレッドは一時停止状態にあり、スレッドプールは自動的にスレッドの一部を破棄し、システムリソースを回収します.
    このような事前作成技術に基づいて、スレッドプールは、スレッドの作成と破棄自体によるオーバーヘッドを各特定のタスクに割り当て、実行回数が多いほど、各タスクが分担するスレッド自体のオーバーヘッドは小さくなりますが、スレッド間の同期によるオーバーヘッドも考慮する必要があります.
    4.ソースコードのテスト
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Random;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class ThreadPool {
        public static void UseThread(int count) {
            long start_time = System.currentTimeMillis();
            final List l = new LinkedList();
            final Random random = new Random();
            for (int i = 0; i < count; ++i) {
                Thread thread = new Thread() {
                    @Override
                    public void run() {
                        l.add(random.nextInt());
                    }
                };
                thread.start();
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(System.currentTimeMillis() - start_time);
            System.out.println(l.size());
        }
    
        public static void UseThreadPool(int count) {
            long start_time = System.currentTimeMillis();
            final List l = new LinkedList();
            ThreadPoolExecutor tp = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, 
                    new LinkedBlockingQueue(count));
            final Random random = new Random();
            for (int i = 0; i < count; ++i) {
                tp.execute(new Runnable() {
                    @Override
                    public void run() {
                        l.add(random.nextInt());
                    }
                });
            }
            tp.shutdown();
            try {
                tp.awaitTermination(1, TimeUnit.DAYS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(System.currentTimeMillis() - start_time);
            System.out.println(l.size());
    
        }
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            System.out.println("Not Use Thread Pool: ");
            UseThread(200000);
            System.out.println("Use Thread Pool: ");
            UseThreadPool(200000);
        }
    
    }
    

    5.テスト結果
    Not Use Thread Pool: 
    13078
    200000
    Use Thread Pool: 
    50
    200000

    同様に20000回実行した場合,スレッドプールを用いた方法は合計50 ms消費され,スレッドプールを用いない方法は13078 ms消費された.もちろん、スレッドで実行される作業は簡単であるため、スレッドの作成と破棄にかかるコストは時間全体に占める割合が大きい.
    リファレンス:スレッドプール