マルチスレッド計算の同期について

序文

デイリー移動レンガ、ビジネスロジックの最適化シリアルコールRPCサービス(私はシリアル理解していない理由を私に聞かないでください)で仕事を得た、骨の折れる反射によって、私は偉大な神を取り巻く生徒に尋ねる、この記事で、知恵で満たさ。

マルチモードスレッドは、結果を取得します

別のマルチスレッドのクエリデータの要約の考えは、私は三つの方法を考え、及び経営成績に基づいて最適なソリューションを選択すること。

将来の同期

  • 今後の同期は、確かに私たちは皆知っている、あるget下記の方法で、ソースコードのコメント。

待機が必要に応じて計算が完了し、その結果の解釈:. Future.get()スレッド・コンピューティングの終了前を取得するためには、待機状態にありました。

コード

public static void main(String[] args) throws InterruptedException, ExecutionException {
        long l = System.currentTimeMillis();
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        Future<Integer> future = executorService.submit(() -> {
            System.out.println("执行耗时操作...");
            timeConsumeOp();
            // 耗时3000ms
            return 100;
        });

        Future<Integer> future1 = executorService.submit(() -> {
            System.out.println("执行耗时操作1...");
            // 耗时2000ms
            timeConsumeOp1();
            return 101;
        });

		// 依次将future、future1添加进列表
        List<Future<Integer>> futureList = Lists.newArrayList(future, future1);
        int s = 0;
        for (Future<Integer> future2 : futureList) {
	        // 线程计算结果
	        System.out.println("返回结果:" + future2.get());
            s = s + future2.get();
        }
        // 求和
        System.out.println("计算结果:" + s);
        // 主线程耗时
        System.out.println("主线程耗时:" + (System.currentTimeMillis() - l));
    }

    private static void timeConsumeOp() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void timeConsumeOp1() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
复制代码
  • 結果
执行耗时操作...
执行耗时操作1...
返回结果:100
返回结果:101
计算结果:201
主线程耗时:3077
复制代码

解決

  • 呼び出しは同期で、時間がかかり、それはメインスレッドから分かる結果のみを実行するために始め戻るのを待って、待ち状態でメインスレッドので、3000ms程度かかります。

  • 結果は、呼び出しシーケンスに依存し得るが、future1より計算時間future短縮(2000ミリ秒<3000ms)は、最初に取得した後、101 100を取得する必要がありますが、Future.get()横断としてシーケンスを呼び出すに依存futureList最初の通話中にfutureするget方法。

  • 私たちは次のようになりますfutureList結果を見て、置換要素の順序。

// 先future1
List<Future<Integer>> futureList = Lists.newArrayList(future1, future);
复制代码
  • 結果
执行耗时操作...
执行耗时操作1...
返回结果:101
返回结果:100
计算结果:201
主线程耗时:3076
复制代码

CompletionService

  • CompletionService使用するexcutorタスクの実行の実現を、追加LinkedBlockingQueue完了FutureTask追加します。
	// 使用线程数为2的线程池初始化
    private static CompletionService<Integer> completionService = new ExecutorCompletionService<> (Executors.newFixedThreadPool(2));
复制代码

コード

public static void main(String[] args) throws InterruptedException, ExecutionException {
        long l = System.currentTimeMillis();
        
        Future<Integer> future = completionService.submit(() -> {
            System.out.println("执行耗时操作...");
            timeConsumeOp();
            return 100;
        });

        Future<Integer> future1 = completionService.submit(() -> {
            System.out.println("执行耗时操作1...");
            timeConsumeOp1();
            return 101;
        });

        int s = 0;

        for (int i = 0; i < 2; i++) {
            int result = completionService.take().get();
            System.out.println("返回结果:" + result);
            s += result;
        }

        System.out.println("计算结果:" + s);
        System.out.println("主线程耗时:" + (System.currentTimeMillis() - l));
    }

    private static void timeConsumeOp() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void timeConsumeOp1() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
复制代码
  • 結果
执行耗时操作...
执行耗时操作1...
返回结果:101
返回结果:100
计算结果:201
主线程耗时:3066
复制代码

解決

  • 呼び出しは同期で、時間がかかり、それはメインスレッドから分かる結果のみを実行するために始め戻るのを待って、待ち状態でメインスレッドので、3000ms程度かかります。
  • 結果は、計算の順序に依存し得る完了したが、future1より計算時間future、次いでこうして101 100を与えるために得られた、短い(2000ミリ秒<3000ms)。

将来のコールバックタイプ+たCountDownLatch

将来のコールバックタイプ

  • 今後のコールバックスタイルを達成するための多くの方法がありますが、Guavaそれは二つの方法を提供しListeningExecutorServiceCompletableFutureそれができるにも使用することNettyFuture
  • コールバックのタイプを理解、Androidの開発経験が場に出たとき、実際には、メインスレッドでのレンダリングを担当するのAndroid、ネットワークへのスレッドアクセスなどの非同期呼び出しは、コールバックの結果はUIを変更するためにメインスレッドをトリガを取得します。
  • 本明細書で使用されるようCompletableFutureな実装を、我々は他の方法で使用することができます興味を持っています。

たCountDownLatch(CycleBarrier)

  • 「カウンター」に似たCountDownLatch countdown()方法は、「カウンター」マイナス1、呼び出すだろうawait()前に0に「カウンタ」内のスレッドを、待機状態にあります。

その結果は保存されています

  • 補正演算式はそう結果を使用することができる同期させるために、作業スレッドの復帰を特徴とするconcurrentセット、又は同様の使用Map<AtomicInteger,Object>データ結果を各サブスレッドの結果を要約します。

コード

public static void main(String[] args) throws Exception{
        CountDownLatch countDownLatch = new CountDownLatch(2);

        List<Integer> list = Collections.synchronizedList(new ArrayList<>(2));
        long l = System.currentTimeMillis();
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("执行耗时操作...");
            timeConsumeOp();
            return 100;
        });
        CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
            @Override
            public Integer get() {
                System.out.println("执行耗时操作1...");
                timeConsumeOp1();
                return 101;
            }
        });

        completableFuture.whenComplete((integer, throwable) -> {

            if (throwable != null) {
                System.out.println("运行错误");
            } else {
                System.out.println("计算结果:" + integer + " 线程名:" + Thread.currentThread().getName());
                list.add(integer);
            }
            countDownLatch.countDown();

        });

        completableFuture1.whenComplete((integer, throwable) -> {

            if (throwable != null) {
                System.out.println("运行错误");
            } else {
                System.out.println("计算结果:" + integer + " 线程名:" + Thread.currentThread().getName());
                list.add(integer);
            }
            countDownLatch.countDown();
        });


        System.out.println("计算结果汇总前 主线程还在运行:" + (System.currentTimeMillis() - l));
        // 主线程等待
        countDownLatch.await();

        int s = 0;
        for (int i : list) {
            s += i;
        }
        System.out.println("计算结果为:" + s + " 耗时 " + (System.currentTimeMillis() - l));
    }
复制代码
  • 結果
执行耗时操作...
执行耗时操作1...
计算结果汇总前 主线程还在运行:70
计算结果:101 线程名:ForkJoinPool.commonPool-worker-2
计算结果:100 线程名:ForkJoinPool.commonPool-worker-1
计算结果为:201 耗时 3072
复制代码

解決

  • コールバックタイプ、それは待機状態でメインスレッドを作成していない非同期呼び出しがあります。
  • countDownLatch.countDown()結果を格納するための時間を計算した後、それ以外の結果は、スレッドの実行を逃すことがあります。
  • countDownLatch.await()すべてのスレッドを待ちますメインスレッドはcountDown、それはあなたが時間のかかる見ることができ、実行を開始します。
  • 使用しCollections.synchronizedList、異なるスレッド、メモリロック保証結果の戻り結果の理由を。

エピローグ

私たちはよりよい解決策を持って、この記事では、私は考えることができるいくつかのプログラムを組み合わせて、簡単な分析は、人々は常に正しいではありません、私は著者と通信することができます。

リファレンス

www.cnkirito.moe/future-and-...

おすすめ

転載: juejin.im/post/5d5556df6fb9a06acf2b5597
おすすめ