事故で爆発するオンラインスレッドプールにつながる、スレッドを虐待

事故の背景

単純なビジネスシナリオを記述する、仕事タイミング分散プロジェクトが存在し、ペンシルバニア州へのデータの定期的なアクセスは、2つのサイクルがあり、第一層は、約1000個のデータであり、第2層は、データサブデータあたり約20サイクル未満でありますその後、マルチスレッドのデータストレージの方法によって焼き取りに行きます。これは、単純なビジネスの背景です。ホワイトは注意を払うに書き込みコードにラインを持って参加しました!

この問題は非常に簡単な解決されるが、私はこの問題のプログラマが遭遇した最初の時間は、またですが、大きな影響は、私は少し要約を行うために必要だと思うと、将来的には小さなパートナーのような書き込みコードになりますこの問題は、私は徹底的にスレッドプールを理解していない同僚によって引き起こされることに注意してください、大きな問題が発生します。私は、スレッド・プールに軽く押し軽く押す前に、問題についてお話しましょう。

スレッドプール

まず私は、一般的にカスタムプールをスレッド化している企業で指摘したいすべての、スレッドプールはめったにので、制御を実現するために、私たちが提供するJDKへのいくつかの方法が使用されていません。

ここでは、それだけの簡単なレビューを支援するために、特定のスレッドプールは、大規模なA大規模なオンラインの記事があります。

  public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) 
   
private static ThreadPoolExecutor poolExecutor = new
            ThreadPoolExecutor(3,
            30,
            1,
            TimeUnit.MINUTES,
            new ArrayBlockingQueue(1000),
            new MyRejectedExecutionHandler(MAX_QUEUE_SIZE));
复制代码

それは、スレッド・プールに来るときに、これらのパラメータを理解する以外の何ものでもありません。

プロセス

  • 我々はcorePoolSizeが5で定義した場合maximunPoolSizeは30、keepAliveTimeが1分、1000バール程度キューです
  • 最初のタスクは、スレッドプール内のキューに置かれてきたキュー内から取り出された、スレッドの現在の数は、スレッドプールが少なくcorePoolSize、オーオー、兄弟よりも、私はあなたが作成あげることがわかった最初のタスクに取得しますスレッド。
  • 2タスクのこの時点で再び、そして中に入るキュースレッドプールから、現在のスレッドが少なくcorePoolSizeよりも見つけ、すぐに継続する新しいスレッドを開きます
  • この時点で、最初の5つのタスクが来て、その後、corePoolSize等しいが、[OK]を、その後、スレッドの作成に進みます。これらの5人のスレッドを作成するためのスレッドプールがスレッドの消費量を作成するために、世代の重複を避けるために理由である、破壊されません。
  • 最初の6タスクが来る。この時点で、corePoolSizeの中に発見スレッドプールのスレッドはちょうど、新しいスレッドを作成するには、この時点で必要がない、実行、および終了し、再利用の効果を直接使用して、。
  • もし私たちのタスクの多くが、また、より多くの時間がかかり、corePoolSizeにはアイドル状態のスレッドを持っていない、maximumPoolSizeは、スレッドプール内corePoolSize << maximumPoolSizeが[タスクNUMを実行するためにスレッドを作成するために進んで、この時間を見つけて、次のステップが再生されます
  • だから、ときスレッドプールNUMの数> maximumPoolSizeが、それはまだキュー内部に直接行く新しいタスクに新しいスレッドを作成しません
  • (ここでは1000で定義された)最大数を超過しているキューは、スレッドプールではなく、我々が満ちていたと新しいタスクを受け取ることができませんでしたし、私は申し訳ありませんが弟だ、私はあなたを拒否することができる場合は、その後、私たちのMyRejectedExecutionHandlerを残しました
  • 既に述べた基本パラメータ上、その後、最大30、例えば5のコアとして、アイドル・スレッドよりも、このシナリオcorePoolSize <NUM <maximumPoolSize corePoolSize生存時間は、この時間をkeepAliveTimeがを離れたときにそれを使用する方法余分な25件のスレッドスレッドを超えて、結論は簡単です:コアスレッドを越えて生存のスレッドのための時間。

次に、上記の説明を行うための全体のコア・プロセス・スレッド・プールです。

カスタムスレッドプール

直接コードに取り付けたカスタム・スレッド・プールに続いて、プロセスと原則スレッドプールについて説明してきた、私はあまり詳しく説明しません。

// 定义一个线程池类
public class MyWorkerThreadPool {

    private static final int MAX_QUEUE_SIZE = 1000;

    private static ThreadPoolExecutor poolExecutor = new
            ThreadPoolExecutor(3,
            30,
            1,
            TimeUnit.MINUTES,
            new ArrayBlockingQueue(MAX_QUEUE_SIZE),
            new MyRejectedExecutionHandler(MAX_QUEUE_SIZE));

    public static void submitTak(Runnable run) {
        poolExecutor.submit(run);
    }

    public static void shutdown() {
        poolExecutor.shutdown();
    }
}

// 定义一个拒绝策略

public class MyRejectedExecutionHandler implements RejectedExecutionHandler{

    private final Log logger = LogFactory.getLog(this.getClass());

    private int  maxQueueSize;

    public MyRejectedExecutionHandler(int maxQueueSize){
        this.maxQueueSize=maxQueueSize;
    }

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println("提交任务失败了" +
                "当前队列最大长度" +
                "maxQueueSize="+maxQueueSize+
                ",maximumPoolSize="+executor.getMaximumPoolSize());
        if(executor.getQueue().size()<maxQueueSize){
            executor.submit(r);
        }else{
            try {
                Thread.sleep(3000);
                executor.submit(r);
            }catch (Exception e){
                //此异常忽略
                executor.submit(r);
            }
        }
    }
}
复制代码

事故コード(疑似コード)

その後、次の焦点は、すでにここに、これはここで、分散の正規の仕事である私の同僚は、それから、コードの派生を合理化することであると述べたとしてのみ、擬似コードの一部を掲載しています。

 //模拟场景
        for(int i= 0;i< 1000;i++){
            for(int j = 0;j<20;j++){
                MyWorkerThreadPool.submitTak(()->{
                    // 真实业务场景这里非常耗时,
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
        


执行结果:
提交任务失败了当前队列最大长度maxQueueSize=1000,maximumPoolSize=30
提交任务失败了当前队列最大长度maxQueueSize=1000,maximumPoolSize=30
提交任务失败了当前队列最大长度maxQueueSize=1000,maximumPoolSize=30
提交任务失败了当前队列最大长度maxQueueSize=1000,maximumPoolSize=30
提交任务失败了当前队列最大长度maxQueueSize=1000,maximumPoolSize=30
复制代码

ここで第一の層は、当社のカスタム・スレッド・キューフル同じ時間で、その結果、1000のデータは、第2層データサイクル30、コードの同僚をシミュレートし、2000 * 30これは、オープンああへのスレッドの数です。慎重に検討されていないひどいので、現在のビジネスの利用につながると他の機能の完全な別のスレッド・プールは、私たちのプロジェクトの非同期メソッドの実行結果を拒否されたとき、多くは実行されて拒否されていないされているかどうかの結果それは想像することができます。

それは、ビジネスの他の機能には影響しませんので、だから、解決する方法を、実際には、非常に単純な、我々は、我々はそれを望んで実行するために10スレッドまで指定できるという時間のかかるタスクを実行します。私ここにいるだけで、このような10件の以上のスレッドは、スレッドを終了するためにしばらく待ってブロックされているとして、カウンタを使用して、コードに直接デクリメントされ

public class TestMain {
    public static void main(String[] args) throws Exception{
        //模拟场景,这是一个分布式定时任务,所以不会存在并发同时执行的问题
        AtomicInteger threadNum = new AtomicInteger(0);
        // 模拟当前第一层有1000数据
        for(int i= 0;i< 1000;i++){
            // 模拟每条线路有20条子数据
            for(int j = 0;j<20;j++){
                // 多线程拉去爬取网上的数据汇总到数据库
                // 一次最多开启10个线程取执行耗时操作,
                while (threadNum.get() > 10){
                    // 可以小睡一会再看是否有资格执行
                    Thread.sleep(500);
                }
                // 小增加1
                threadNum.incrementAndGet();
                int tempI = i;
                int tempJ = j;
                MyWorkerThreadPool.submitTak(()->{
                    // 真实业务场景这里非常耗时,
                    try {
                        Thread.sleep(5000);
                         System.out.println(Thread.currentThread().getName()+"执行完第一层数据"+ tempI +"第二层:"+tempJ);
                        // 执行完减少一个1
                        threadNum.decrementAndGet();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }


执行结果:
pool-2-thread-2执行完第一层数据0第二层:1
pool-2-thread-1执行完第一层数据0第二层:0
pool-2-thread-3执行完第一层数据0第二层:2
pool-2-thread-2执行完第一层数据0第二层:3
pool-2-thread-3执行完第一层数据0第二层:5
pool-2-thread-1执行完第一层数据0第二层:4
pool-2-thread-3执行完第一层数据0第二层:7
pool-2-thread-1执行完第一层数据0第二层:8
pool-2-thread-2执行完第一层数据0第二层:6
pool-2-thread-1执行完第一层数据0第二层:10
pool-2-thread-3执行完第一层数据0第二层:9
pool-2-thread-2执行完第一层数据0第二层:11
pool-2-thread-3执行完第一层数据0第二层:13
pool-2-thread-2执行完第一层数据0第二层:14
pool-2-thread-1执行完第一层数据0第二层:12

复制代码

概要

実際には、私たちの開発は、小さな細部をたくさん持っているが、我々は時々注意を払っていないか、その原則、比較的悪い結果をもたらすでしょう時々書き込みコードを知りません。さて、今日のこのここに!

おすすめ

転載: juejin.im/post/5dcf482ff265da0be347b8d4