【发布时间】:2015-09-15 03:40:38
【问题描述】:
我需要通过多个线程更新全局 BigInteger 值 -- BigInteger 线程安全吗?
【问题讨论】:
-
BigInteger 是否线程安全与您无关,因为它是不可变的。所以你必须用另一个替换对 BigInteger 的引用来更新它。而且您需要确保您以线程安全的方式执行此操作。
标签: java biginteger
我需要通过多个线程更新全局 BigInteger 值 -- BigInteger 线程安全吗?
【问题讨论】:
标签: java biginteger
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))));
【讨论】:
final AtomicReference<BigInteger> bsum = new AtomicReference<BigInteger>(BigInteger.ZERO); Arrays.asList(numbers).forEach(number -> {bsum.set(bsum.get().add(new BigInteger(number)));});
您无法更新 BigInteger,请阅读 javadoc,它是不可变的任意精度整数。。要更新 BigInteger 字段,您需要将其替换为新的 BigInteger,如下所示
...
bigInteger = bigInteger.add(value);
...
这需要同步,否则可能会发生两件事:
一个线程更改了值,但其他线程看不到更改
两个或多个线程同时添加,但一些添加丢失。
【讨论】:
java.math.BigInteger 是不可变的,因此它可以安全地被多个线程访问。
旁注:Thread-safety 是一个术语,用于处理多个线程访问一段代码(不仅仅是一个变量)。
【讨论】:
BigInteger 是线程安全的。 但是你想更新它,你应该使用这样的代码:
private BigInteger bigInteger = BigInteger.ONE;
public void add(BigInteger plus) {
bigInteger = bigInteger.add(plus);
}
所以同步是必要的。
【讨论】:
BigInteger:不可变任意精度整数。
Immutable Objects : 如果一个对象的状态在构造后不能改变,则该对象被认为是不可变的。最大程度地依赖不可变对象被广泛接受为创建简单、可靠代码的合理策略。
这意味着 BigInteger 是线程安全的。
【讨论】: