原子方法在 Java 中真的是全有还是全无?

问题描述 投票:0回答:1

这个问题更多的是关于术语。我使用原子作为并发术语,我这样描述它:“如果多个线程并发或顺序调用相同的方法,如果该方法是原子的,结果将是相同的”,而我使用事务更像是一个工作单元术语.事务性作业要么成功要么失败,并且不会有中间脏状态。

另一方面,我看到了这篇文章,它声称“一个方法是原子的,如果它是‘all or nothing’。如果一个线程读取数据,线程只能看到执行之前或之后的状态原子方法——没有中间状态。原子方法执行成功后,变化对所有线程可见。原子方法只修改自己对象的数据,没有副作用。下面是一些原子方法的例子。”在我看来,这个定义不一定正确。例如下面的代码是原子的,但我认为有副作用。

    public synchronized void add(){
        x++;
        if(System.currentTimeMillis()%10==0){
            throw new RuntimeException();
        }
        y++;
    }

我对术语的理解是否正确?

java concurrency transactions atomic
1个回答
0
投票

...看到这篇文章..."all or nothing"...[另一个]线程只能看到执行前或执行后的状态

就个人而言,我不同意该描述中“全有或全无”的部分,但我同意第二部分。如果线程 A 执行真正的原子操作,那么与线程 A 共享变量的任何其他线程 B 应该只能看到这些变量处于操作开始之前的状态,或者处于线程 A 完成操作之后的状态.线程 B 永远无法看到处于完成一半状态的变量。

我认为是模棱两可的“全有或全无”让你想到交易。

您的示例代码在 10% 的时间内抛出异常。如果您将该异常用作“失败”的模型,并且您期望

x++
操作在失败发生时将“展开”,那么它不会这样工作。 “原子的”和“事务的”是相关的,因为事务的完成必须是原子的,但是这两个词的意思不同

如果你想保证

x
y
总是收到相同数量的增量,即使在失败的情况下,那么直到有人给我们真正的交易,你必须编写明确的代码来提供这种保证:

    public synchronized void add(){
        x++;
        try {
            someOperationThatMightFail();
        }
        catch (Exception e) {
            --x;
            throw e;
        }
        y++;
    }

...下面的代码是原子的...

这不是真的原子的。它可以是 effectively 原子的,但前提是没有其他线程访问相同的

x
y
除非
synchronized
在同一个对象上。如果它真的是原子的,那么在 any 条件下,另一个线程不可能在
x
调用仅完成一半的时刻观察
y
add
。但是,如果程序员保证在访问这些变量时每个其他线程都在同一个对象上同步,那么它具有相同的效果就好像它真的是原子的一样。

...但是它有副作用...

我没有看到其中的“但是”。除非线程之间共享变量,否则原子性毫无意义。你所说的“副作用”是变量

x
y
是如何共享的。

© www.soinside.com 2019 - 2024. All rights reserved.