有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java是否有可能以线程安全的方式安全地递增BigInteger,可能是使用AtomicReference,不带锁?

我们的很多代码都是遗留的,但我们正在转向“大数据”后端,我正在努力宣传新的API调用,鼓励使用最新的Spring库等。我们的问题之一是应用层ID生成。出于我不明白的原因,一个更高的权威想要序列的BigInteger。我本可以通过重新生成并重新尝试失败的插入来随机设置它们,但我确实被否决了

撇开牢骚不谈,我现在的处境是,我需要在线程之间递增并获得一个大整数,并以安全且高效的方式执行它。我以前从未使用过AtomicReference,但它看起来非常适合这个应用程序。现在我们有一个同步的代码块,这严重影响了我们的性能

走这条路对吗?语法示例

我应该提到的是,这个模块的工作方式是,它使用存储过程访问数据库,以获取一系列要使用的值。一次数万次,所以可能每20分钟只发生一次。这可以防止不同的服务器相互攻击,但也增加了必须将BigInteger设置为任意后续值的麻烦。当然,这也需要线程安全

另外,我仍然认为我的随机生成想法比处理所有这些线程的东西要好。BigInteger是一个非常大的数字,两次生成同一个数字的几率必须接近零


共 (2) 个答案

  1. # 1 楼答案

    使用Java8中引入的^{}^{},这将变得更易于管理和理解。通过提供一个累加器函数,可以将值设置为函数的结果,并根据需要返回之前的结果或计算结果,从而自动更新值。下面是一个关于该类的示例,接下来是我编写的一个使用该类的简单示例:

    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
    
  2. # 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()的原子长代码的副本