並行プログラミング(II):スレッドプールの分析

スレッドプールで


説明

スレッドを作成し、起動し、破壊など非常にリソースを消費するプロセスです。スレッドプールにつながります。

スレッドプールのアクション

  1. リソース消費量を削減し、すでに作成されたスレッドを再利用します。
  2. 応答時間を改善し、ミッションは待たずに、すでにスレッドによって直接作成着きました。
  3. 統一されたスレッド管理、スレッドの統一分布、監視およびチューニング(フルタイムの人)。

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

まず、作成

現在、作成した6つのスレッドプールがあります。作成するための4つの一般的な方法を説明するためにまず、本質的には、それはコンストラクタのパラメータを変更することにより、別のスレッドを作成することです。コンストラクタ呼び出しは最終的なもの:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

以下のスレッドプールへのデモを作成するための命令です。

1、キャッシュスレッドを作成
 ExecutorService threadPool = Executors.newCachedThreadPool();
 for(int i=0;i<10;i++){
     int temp = i;
     threadPool.execute(new Runnable() {
         @Override
         public void run() {
             System.out.println(Thread.currentThread().getName()+","+temp);
         }
     });
 }
 threadPool.shutdown();// 停掉线程池
2.長さ(一般的な)固定スレッドを作成
  ExecutorService threadPool = Executors.newFixedThreadPool(3);
  for (int i = 0; i < 10; i++) {
      int temp = i;
      threadPool.execute(new Runnable() {
          @Override
          public void run() {
              System.out.println(Thread.currentThread().getName()+","+temp);
          }
      });
  }
  threadPool.shutdown();// 停掉线程池
3.定期的にスレッドを作成します。
 ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);
 threadPool.schedule(new Runnable() {
     @Override
     public void run() {
         System.out.println("我是定时线程,三秒后启动");
     }
 },3, TimeUnit.SECONDS); // 第一个参数是任务,第二个参数是时间长度,第三个参数时间单位
 threadPool.shutdown();// 停掉线程池
図4に示すように、シングルスレッドを作成します
 ExecutorService threadPool = Executors.newSingleThreadExecutor();
 for (int i = 0; i < 10; i++) {
     threadPool.execute(new Runnable() {
         @Override
         public void run() {
             System.out.println("我是单线程线程");
         }
     });
 }
 threadPool.shutdown();// 停掉线程池
第二に、スレッドプールのパラメータ説明

キー:ほとんどインタビューが求められます。

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

corePoolSize:スレッドのコア数

maximumPoolSize:スレッドの最大数

keepAliveTimea:アイドル時間をスレッド

単位:時間のTimeUnitで列挙型の値を代表keepAliveTimeがユニットには、次の値をとることができます。

  • TimeUnit.DAYS; //天   
  • TimeUnit.HOURS; //時間   
  • TimeUnit.MINUTES; //分   
  • TimeUnit.SECONDS; //秒   
  • TimeUnit.MILLISECONDS; //ミリ秒   
  • TimeUnit.MICROSECONDS; //微妙   
  • TimeUnit.NANOSECONDS; // NS

ワークキュー:ブロックキューが実行されるのを待っている店舗のタスクに使用され、キューのスレッドプール戦略、次の値を決定します。

  • ArrayBlockingQueue
  • LinkedBlockingQueue
  • SynchronousQueue

threadFactory:スレッドファクトリは、スレッドを作成するために使用されます

ハンドラ:スレッドが政策を否定します。スレッドがmaximumPoolSizeを超えて作成され、バッファキューがいっぱいになるされた場合、新しいタスクは、次の値を拒否します:

  • ThreadPoolExecutor.AbortPolicy:RejectedExecutionExceptionタスクを破棄し、例外をスローします。
  • ThreadPoolExecutor.DiscardPolicy:タスクが破棄されますが、例外をスローしません。   
  • ThreadPoolExecutor.DiscardOldestPolicy:一番のタスクキューを破棄して、タスクを再実行してみてください(この手順を繰り返します)   
  • ThreadPoolExecutor.CallerRunsPolicy:呼び出し側のスレッドでタスクを処理
第三に、スレッドを作成するプロセス

図は、実行スレッドプールの流れです。


プロセスの説明:

  1. ユーザーは、スレッド・プールがいっぱいになるコアを決定することで、ジョブ、第一のコアスレッドプールを提出します。

  2. コアスレッドプールが満杯ではありませんどのように、タスクの実行スレッド、コアスレッドがいっぱいになるかは、次のステップを取ります。

  3. キャッシュキューのスレッドには、バッファ・キューが満杯であるか否かを判断します。

  4. スレッドバッファキューがいっぱいの場合、最大スレッドプールを入力してください。

  5. 最大スレッドプールが満杯になった場合は、スレッドのタスクを作成します。

  6. 最大スレッドプールが満杯になった場合、拒否されました。

第二のスレッド・プールを作成します。

5、newWorkStealingPool
ExecutorService m = Executors.newWorkStealingPool(2);

並列度などのパラメータを渡していない場合1)並列度を持つスレッドプールを作成し、並列度が、実行中のスレッドの同じ数までの時間を決定し、CPUの数は、現在のシステムにデフォルト設定されます。

2)各スレッドは他のタスクを取得するための独自のイニシアチブで、タスクの実行が終わって、独自のキューを維持します。

3)生成デーモンスレッドです。

4)しないThreadPoolExecutor拡張はForkJoinPoolの拡張です。

5)デモ:

public class Main {
    public static void main(String[] args) throws Exception {
        // 设置并行级别为2,即默认每时每刻只有2个线程同时执行
        ExecutorService m = Executors.newWorkStealingPool(2);
        for (int i = 1; i <= 10; i++) {
            final int count=i;
            m.submit(new Runnable() {
                @Override
                public void run() {
                    Date now=new Date();
                    System.out.println("线程" + Thread.currentThread() + "完成任务:"+ count+"   时间为:"+    now.getSeconds());
                    try {
                        Thread.sleep(1000);//此任务耗时1s
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }); 
        }
        while(true){
            //主线程陷入死循环,来观察结果,否则是看不到结果的
        }
    }
}
6、ForkJoinPool

アイデアは(ビジネス要件に基づいて、粒子サイズを制御するために分割することができます)小さなタスクに大きなタスクを壊すプール、再帰的思考の使用を、スレッド化にあります。プレゼンテーションの顔の下には質問:長さ1,000,000配列要素未満百乱数である、様々な要素が追加されます。

public class ForkJoin {
    static int[] nums = new int[1000000];
    static final int MAX_NUM = 50000;
    private static Random random = new Random();
    static {
        for (int i = 0; i < nums.length; i++) {
            nums[i] = random.nextInt(100);
        }
        System.out.println(Arrays.stream(nums).sum()); // 传统的方式计算
    }
    // 递归思想,不断将大任务分成小任务。
    // RecursiveAction 无返回值;RecursiveTask 有返回值。
    static class AddTask extends RecursiveAction{
        int start,end;
        AddTask(int start, int end) {
            this.start = start;
            this.end = end;
        }
        @Override
        protected void compute() {
            if(end-start<=MAX_NUM){
                long sum = 0L;
                for (int i = start; i < end; i++) {
                    sum += nums[i];
                }
                System.out.println("from+"+start+"to"+end+"="+sum);
            }else{
                int middle =start + (end-start)/2;
                AddTask addTask = new AddTask(start,middle);
                AddTask addTask2 = new AddTask(middle,end);
                addTask.fork();
                addTask2.fork();
            }
        }
    }
    public static void main(String[] args) throws IOException {
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        AddTask addTask = new AddTask(0,nums.length);
        forkJoinPool.execute(addTask);
        System.in.read();
    }
}

これの利点は、スレッドプールは、マルチCPUを利用することができるということである、マルチコアCPUの利点は、タスクが「小さなタスク」、コアプロセッサの複数に並行して実行される「小タスク」の複数の複数に分割され、場合実行が完了した後、いくつかの「小さなタスクが」、その後、これらの結果をマージすることができ。

おすすめ

転載: www.cnblogs.com/xzy-/p/10928978.html
おすすめ