【问题标题】:Is BigInteger Thread safe?BigInteger 线程安全吗?
【发布时间】:2015-09-15 03:40:38
【问题描述】:

我需要通过多个线程更新全局 BigInteger 值 -- BigInteger 线程安全吗?

【问题讨论】:

  • BigInteger 是否线程安全与您无关,因为它是不可变的。所以你必须用另一个替换对 BigInteger 的引用来更新它。而且您需要确保以线程安全的方式执行此操作。

标签: java biginteger


【解决方案1】:

BigInteger 对象是immutable objects 的代表示例。
简单来说:

每个不可变的对象都是线程安全的,但是对它的引用 is not.

对于不可变对象,状态在整个生命周期内都是固定的。因为没有更改它的选项,因此每个“更改”操作都相当于替换为一个新对象。因此,在 N 个线程对特定引用并行执行一系列修改后,结果值很难预测(某些更新可能会丢失 - 未被注意到)。
Integer 类也是如此。为了克服这一限制,AtomicInteger 类被引入JDK5
不幸的是,JDK 中没有“AtomicBigInteger”类。另一种解决方案是用 AtomicReference 包装一个对象实例——它作为一个代理,使所有操作同步和原子化。

我提出以下需要 JDK 8 的紧凑型解决方案:

final AtomicReference<BigInteger> valueHolder = new AtomicReference(BigInteger.ZERO);

使用这种方法,BigInteger 提供的任何方法都可以重新表述为 lambda 表达式,例如:

valueHolder.updateAndGet(x -> x.add(BigInteger.valueOf(10)));

要检查解决方案是否正确,您可以使用以下代码 sn-p 使用并行流对所有小于 100 的整数求和(它是多线程操作):

IntStream.range(0, 100).parallel()
        .forEach(i -> valueHolder.updateAndGet(x -> x.add(BigInteger.valueOf(i))));

【讨论】:

  • 这并不能使其本身成为线程安全的,因为在 AtomicReference 上没有原子算术运算。您可以使用 compareAndSet 来实现旋转方法,但您需要为此扩展您的答案。或者,如果您想建议使用 jdk1.8 功能,您也应该将其添加到您的答案中。
  • @ErwinBolwidt 感谢您的建议。我刚刚改进了代码示例。
  • 效果很好。我试图对列表中的每个数字求和,可能的总和超过 int/long 限制,所以我调整了代码并使用:final AtomicReference&lt;BigInteger&gt; bsum = new AtomicReference&lt;BigInteger&gt;(BigInteger.ZERO); Arrays.asList(numbers).forEach(number -&gt; {bsum.set(bsum.get().add(new BigInteger(number)));});
【解决方案2】:

您无法更新 BigInteger,请阅读 javadoc,它是不可变的任意精度整数。。要更新 BigInteger 字段,您需要将其替换为新的 BigInteger,如下所示

...
bigInteger = bigInteger.add(value);
...

这需要同步,否则可能会发生两件事:

  1. 一个线程更改了值,但其他线程看不到更改

  2. 两个或多个线程同时添加,但一些添加丢失。

【讨论】:

    【解决方案3】:

    java.math.BigInteger 是不可变的,因此它可以安全地被多个线程访问。

    旁注:Thread-safety 是一个术语,用于处理多个线程访问一段代码(不仅仅是一个变量)。

    【讨论】:

    • 与菜鸟交谈时,请注意将“线程安全”与“代码”联系起来。当我们谈论某些代码体(例如,Java 类)的“线程安全”时,我们谈论的是代码在多线程环境中操作的 data 会发生什么.很多菜鸟都在谈论“同步方法”和“同步块”,却根本不知道需要保护的是数据。
    • @jameslarge 代码本身显然是无状态的,除非它不是不可变的。我已经很长时间没有遇到“自我修改代码”了...... :)
    【解决方案4】:

    BigInteger 是线程安全的。 但是你想更新它,你应该使用这样的代码:

    private BigInteger bigInteger = BigInteger.ONE;
    
    public void add(BigInteger plus) {
        bigInteger = bigInteger.add(plus);
    }
    

    所以同步是必要的。

    【讨论】:

    • 源文档显示 BigInteger 是线程安全的(或证明它的代码示例)将大大增强这个答案。欢迎使用 StackOverflow!
    【解决方案5】:

    BigInteger不可变任意精度整数。

    Immutable Objects : 如果一个对象的状态在构造后不能改变,则该对象被认为是不可变的。最大程度地依赖不可变对象被广泛接受为创建简单、可靠代码的合理策略

    这意味着 BigInteger 是线程安全的。

    【讨论】:

      猜你喜欢
      • 2020-04-15
      • 2011-07-04
      • 2014-04-26
      • 2012-11-30
      • 2010-12-30
      • 2013-03-12
      • 2021-08-03
      • 2010-12-27
      • 2018-06-04
      相关资源
      最近更新 更多