java 変数 おそらくAtomicReferenceを使用して、ロックなしでスレッドセーフな方法でBigIntegerを安全にインクリメントすることは可能ですか?



java スレッドセーフでない (2)

私たちのコードの多くはレガシーですが、「ビッグデータ」バックエンドに移行しつつあります。新しいAPI呼び出しを広め、最新のSpringライブラリなどの使用を奨励しようとしています。問題の1つはアプリケーションレイヤーIDです。世代。 私が理解していない理由のために、より高い権威は順次BigIntegerのものを望んでいます。 失敗した挿入を再生成して再試行してランダムにしたはずですが、拒否されました。

厄介なことに、私はスレッド間でBigIntegerをインクリメントして取得し、安全でパフォーマンスの高い方法で実行する必要がある立場にいます。 私は今までAtomicReferenceを使ったことがないが、このアプリケーションには完璧に近いようだ。 今のところ、同期化されたコードブロックがあるため、パフォーマンスがかなり低下しています。

これは正しいやり方ですか? 構文例は?

このモジュールがどのように機能するかは、使用する値の範囲を取得するためにストアドプロシージャを使用してデータベースにアクセスすることに注意してください。 一度に何万もの回数が発生するため、20分に1回だけ発生する可能性があります。 これにより、さまざまなサーバーが互いに踏み合わないようになりますが、BigIntegerを任意の値に設定しなければならないというしわが追加されます。 もちろん、これもスレッドセーフである必要があります。

PS私はまだ私のランダム生成のアイデアは、このすべてのスレッディング処理を処理するよりも優れていると思います。 BigIntegerは途方もなく大きな数であり、同じものを2回生成する可能性はゼロに近い必要があります。


これは、Java 8で導入されたaccumulateAndGetまたはgetAndAccumulateを使用するaccumulateAndGet 、より管理しやすくなり、理解しやすくなります。これらは、関数の結果に値を設定するアキュムレータ関数を提供することによって値をアトミックに更新できます。あなたが必要とするものに応じて結果。 これがそのクラスがどのように見えるかの例であり、それを使用した私が書いた簡単な例が続きます。

import java.math.BigInteger;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

public final class AtomicBigInteger {

  private final AtomicReference<BigInteger> bigInteger;

  public AtomicBigInteger(final BigInteger bigInteger) {
    this.bigInteger = new AtomicReference<>(Objects.requireNonNull(bigInteger));
  }

  // Method references left out for demonstration purposes
  public BigInteger incrementAndGet() {
    return bigInteger.accumulateAndGet(BigInteger.ONE, (previous, x) -> previous.add(x));
  }

  public BigInteger getAndIncrement() {
    return bigInteger.getAndAccumulate(BigInteger.ONE, (previous, x) -> previous.add(x));
  }

  public BigInteger get() {
    return bigInteger.get();
  }
}

それを使った例:

import java.math.BigInteger;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class ABIExample {

  private static final int AVAILABLE_PROCS = Runtime.getRuntime().availableProcessors();
  private static final int INCREMENT_AMOUNT = 2_500_000;
  private static final int TASK_AMOUNT = AVAILABLE_PROCS * 2;
  private static final BigInteger EXPECTED_VALUE = BigInteger.valueOf(INCREMENT_AMOUNT)
                                                             .multiply(BigInteger
                                                                           .valueOf(TASK_AMOUNT));

  public static void main(String[] args)
      throws InterruptedException, ExecutionException {
    System.out.println("Available processors: " + AVAILABLE_PROCS);


    final ExecutorService executorService = Executors
        .newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    final AtomicBigInteger atomicBigInteger = new AtomicBigInteger(BigInteger.ZERO);

    final List<Callable<Void>> incrementTasks =  IntStream.rangeClosed(1, TASK_AMOUNT)
             .mapToObj(i -> incrementTask(i, atomicBigInteger))
             .collect(Collectors.toList());
    final List<Future<Void>> futures = executorService.invokeAll(incrementTasks);
    for (Future<Void> future : futures) {
      future.get();
    }
    executorService.shutdown();
    executorService.awaitTermination(30, TimeUnit.SECONDS);
    System.out.println("Final value: " + atomicBigInteger.get());
    final boolean areEqual = EXPECTED_VALUE.equals(atomicBigInteger.get());
    System.out.println("Does final value equal expected? - " + areEqual);
  }

  private static Callable<Void> incrementTask(
      final int taskNumber,
      final AtomicBigInteger atomicBigInteger
  ) {
    return () -> {
      for (int increment = 0; increment < INCREMENT_AMOUNT; increment++) {
        atomicBigInteger.incrementAndGet();
      }
      System.out.println("Task #" + taskNumber + " Completed");
      return null;
    };

  }
}

そして私のマシン上で例を実行した結果の出力は以下のとおりです。

Available processors: 8
Task #3 Completed
Task #8 Completed
Task #7 Completed
Task #6 Completed
Task #5 Completed
Task #2 Completed
Task #4 Completed
Task #1 Completed
Task #9 Completed
Task #10 Completed
Task #11 Completed
Task #13 Completed
Task #16 Completed
Task #12 Completed
Task #14 Completed
Task #15 Completed
Final value: 80000000
Does final value equal expected? - true

AtomicReferenceを使うことは可能です。

public final class AtomicBigInteger {

    private final AtomicReference<BigInteger> valueHolder = new AtomicReference<>();

    public AtomicBigInteger(BigInteger bigInteger) {
        valueHolder.set(bigInteger);
    }

    public BigInteger incrementAndGet() {
        for (; ; ) {
            BigInteger current = valueHolder.get();
            BigInteger next = current.add(BigInteger.ONE);
            if (valueHolder.compareAndSet(current, next)) {
                return next;
            }
        }
    }
}

これは基本的にincrementAndGet() AtomicLongコードのコピーです。





atomicreference