2012-12-22 3 views
12

私たちのコードの多くは残念ですが、私たちは "Big Data"バックエンドに移行しており、新しいAPI呼び出しを伝えようとしています。最新のSpringライブラリの使用を奨励しています。レイヤーIDの生成。私が理解できない理由のために、より高い権限がシーケンシャルBigInteger'sを望んでいます。私はそれらを無作為にして再生成し、失敗した挿入を再試行しただろうが、私は拒否された。安全にBigIntegerをスレッドセーフな方法でインクリメントできます。おそらくAtomicReference、w/o lockingを使用していますか?

私は増分してスレッド間でBigIntegerを取得し、安全かつ効率的な方法でそれを行う必要がある位置にいます。これまでAtomicReferenceを使ったことはありませんでしたが、このアプリケーションにはかなり近いと思われます。今私たちはコードブロックを同期させているので、パフォーマンスがかなり悪くなります。

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

このモジュールの仕組みは、ストアドプロシージャを使用してデータベースにヒットし、使用する値の範囲を取得します。一度に20万人に1回しか起こらないように、一度に数十万人。これにより、さまざまなサーバーが互いを踏み出すことなく、BigIntegerを任意の後続の値に設定しなければならないというしわが加わります。もちろん、スレッドセーフである必要もあります。

P.S.私はまだ私のランダム世代のアイデアは、このすべてのスレッドのものを処理するよりも良いと思う。 BigIntegerは非常に多く、同じものを2回生成する確率はゼロに近い必要があります。

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()

+2

「AtomicReference」のように思われませんロックしないであなたのためです。しかし、longより大きい整数が必要な場合を除いて、BigIntegerがあなたの速度を落とさないなら、私は驚くでしょう。 AtomicLongに行くように彼らに説得してみてください。 –

+0

私はそれを調べます。いずれにせよ、私はまだ専門的にはその答えが不思議です。 ;-)特に私が再び拒否するかもしれないから。政治。 – user447607

+0

AtomicReference(他のAtomicクラスと同様)はロックを使用しません。 – bowmore

答えて

12

は、迅速なドラフトです。 (私にそれを示した人はそれが起こることはありません主張が)

個人的に私は、可能な場合、私はAtomicLongとなるだろうとして、理論的には飢餓をスレッド化につながる可能性が

private AtomicReference<BigInteger> ref = new AtomicReference<BigInteger>(BigInteger.ZERO); 

public BigInteger incrementAndGet() { 
    BigInteger currVal, newVal; 
    do { 
     currVal = ref.get(); 
     newVal = currVal.clone(); 
     newVal.add(BigInteger.ONE); 
    } while (!ref.compareAndSet(currVal, newVal)); 

} 

をこのコードを好きではありません。

+0

私が書いていたときにこの回答に気付かなかった: –

+0

いつも私に起こる:) – bowmore

+0

はい、私はちょうどそのコードを見ていた: http://www.docjar.com/html/ api/java/util/concurrent/atomic/AtomicLong.java.html – user447607

0

操作がアトミックになるまでこれは基本的に何度も試みるためAtomicLongコードのコピーである:それはここAtomicReferenceを使用して可能である

+0

「Java Concurrency in Practice」:「理論的には、他のスレッドがCASレースで勝利し続けている場合、理論的には何度も試行しなければならない可能性があります。 – bowmore

+1

はい、私は知っていますが、それは間違っていると感じています:) –

+0

@AviramSegal: "クローン"の軽いタイプミスです。飢餓に関しては、優先度の低いスレッドが複数の優先度の高いスレッドと競合しなければならない可能性がありますが、バックオフとリトライのアルゴリズムとは異なり、少なくとも*誰か*が進歩します。 – supercat

3

これは、Java 8で導入されたaccumulateAndGetまたはgetAndAccumulateを使用すると、管理が容易でわかりやすくなります。これらの機能を使用すると、関数の結果に値を設定するアキュムレータ関数を提供し、あなたが必要なものに応じて前回の計算結果を計算します。

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 
+0

私は前の答えを理解していますが、これは理解していません。私はまだ " - >"を使用する機会はあまりありませんでしたので、それは理由かもしれません。 「前」と「x」はどこから来たのですか? – user447607

+0

@ user447607 Java 8ではラムダ式が導入されました。 Oracleからのチュートリアルがあります(ここでは説明できないほど包括的です)(https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html)。私はそれを通過することをお勧めします。基本的に、 'AtomicReference'の' * accumulate * 'メソッドは、アトミックな方法でメソッドを呼び出す' BinaryOperator'を渡すことができます。 'BinaryOperator'は' @ FunctionalInterface'です。 'previous'と' x'は呼び出されるメソッドのパラメータです。上記の場合、両方のパラメータがメソッドに渡されます。 – mkobit

+0

@mkobit:あなたのインクリメントメソッドは 'getAndUpdate' /' updateAndGet'でもっとはっきりと書くことができると思います: 'bigInteger.getAndUpdate(BigInteger.ONE::add)' – Lii

関連する問題