java原始整数是设计的还是偶然的原子? [英] Are java primitive ints atomic by design or by accident?

查看:16
本文介绍了java原始整数是设计的还是偶然的原子?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

就此而言,java 原始整数 (int) 是原子的吗?两个线程共享一个 int 的一些实验似乎表明它们,但当然没有证据表明它们不是并不意味着它们是.

Are java primitive integers (int) atomic at all, for that matter? Some experimentation with two threads sharing an int seems to indicate that they are, but of course absence of evidence that they are not does not imply that they are.

具体来说,我运行的测试是这样的:

Specifically, the test I ran was this:

public class IntSafeChecker {
    static int thing;
    static boolean keepWatching = true;

    // Watcher just looks for monotonically increasing values   
    static class Watcher extends Thread {
        public void run() {
            boolean hasBefore = false;
            int thingBefore = 0;

            while( keepWatching ) {
                // observe the shared int
                int thingNow = thing;
                // fake the 1st value to keep test happy
                if( hasBefore == false ) {
                    thingBefore = thingNow;
                    hasBefore = true;
                }
                // check for decreases (due to partially written values)
                if( thingNow < thingBefore ) {
                    System.err.println("MAJOR TROUBLE!");
                }
                thingBefore = thingNow;
            }
        }
    }

    // Modifier just counts the shared int up to 1 billion
    static class Modifier extends Thread {
        public void run() {
            int what = 0;
            for(int i = 0; i < 1000000000; ++i) {
                what += 1;
                thing = what;
            }
            // kill the watcher when done
            keepWatching = false;
        }
    }

    public static void main(String[] args) {
        Modifier m = new Modifier();
        Watcher w = new Watcher();
        m.start();
        w.start();
    }
}

(仅在 32 位 Windows PC 上使用 java jre 1.6.0_07 进行了尝试)

(and that was only tried with java jre 1.6.0_07 on a 32bit windows PC)

本质上,Modifier 将一个计数序列写入共享整数,而 Watcher 会检查观察到的值是否永远不会减少.在必须将 32 位值作为四个单独的字节(或什至两个 16 位字)访问的机器上,Watcher 可能会捕获处于不一致、半更新状态的共享整数,并检测到值在减少而不是增加.无论(假设的)数据字节是从 LSB 1st 还是 MSB 1st 收集/写入,这都应该有效,但充其量只是概率性的.

Essentially, the Modifier writes a count sequence to the shared integer, while the Watcher checks that the observed values never decrease. On a machine where a 32 bit value had to be accessed as four separate bytes (or even two 16bit words), there would be a probability that Watcher would catch the shared integer in an inconsistent, half-updated state, and detect the value decreasing rather than increasing. This should work whether the (hypothetical) data bytes are collected/written LSB 1st or MSB 1st, but is only probablistic at best.

考虑到当今广泛的数据路径,32 位值可能是有效的原子值,这似乎很有可能,即使 Java 规范不需要它.事实上,对于 32 位数据总线,与 32 位整数相比,您可能需要更加努力地获得对字节的原子访问.

It would seem very probable given today's wide data paths that a 32 bit value could be effectively atomic, even if the java spec doesn't require it. In fact, with a 32 bit data bus it would seem that you might have to work harder to get atomic access to bytes than to 32 bit ints.

谷歌搜索java 原始线程安全"会发现关于线程安全类和对象的大量内容,但寻找原始信息的信息似乎是大海捞针.

Googling on "java primitive thread safety" turns up loads of stuff on thread-safe classes and objects, but looking for the info on the primitives seems to be looking for the proverbial needle in a haystack.

推荐答案

Java 中的所有内存访问默认都是原子的,除了 longdouble(它们可能是原子的,但不一定是).老实说,这不是很清楚,但我相信这就是其中的含义.

All memory accesses in Java are atomic by default, with the exception of long and double (which may be atomic, but don't have to be). It's not put very clearly to be honest, but I believe that's the implication.

来自 第 17.4 节.JLS 的 3:

在一个顺序一致的执行,有一个总订单所有单个操作(例如读取和写)这与程序的顺序,以及每个个人行动是原子的,是立即对每个线程可见.

Within a sequentially consistent execution, there is a total order over all individual actions (such as reads and writes) which is consistent with the order of the program, and each individual action is atomic and is immediately visible to every thread.

然后在 17.7:

一些实现可能会发现它方便分单写对 64 位 long 或 double 的操作将值转换为两个写入操作相邻的 32 位值.为了为了效率,这种行为是具体实施;Java 虚拟的机器可以自由地执行写入long 和 double 值原子地或分为两部分.

Some implementations may find it convenient to divide a single write action on a 64-bit long or double value into two write actions on adjacent 32 bit values. For efficiency's sake, this behavior is implementation specific; Java virtual machines are free to perform writes to long and double values atomically or in two parts.

请注意,原子性与波动性非常不同.

Note that atomicity is very different to volatility though.

当一个线程将整数更新为 5 时,可以保证另一个线程不会看到 1 或 4 或任何其他中间状态,但如果没有任何显式易失性或锁定,另一个线程可能永远看到 0.

When one thread updates an integer to 5, it's guaranteed that another thread won't see 1 or 4 or any other in-between state, but without any explicit volatility or locking, the other thread could see 0 forever.

关于努力获得对字节的原子访问,您是对的:VM 可能必须努力尝试……但它确实必须这样做.来自第 17.6 节规格:

With regard to working hard to get atomic access to bytes, you're right: the VM may well have to try hard... but it does have to. From section 17.6 of the spec:

某些处理器不提供写入单个字节的能力.它实现字节将是非法的此类处理器上的数组更新只需阅读整个单词,更新适当的字节,和然后将整个单词写回记忆.这个问题有时被称为单词撕裂,以及无法轻松更新的处理器单字节隔离其他一些将需要方法.

Some processors do not provide the ability to write to a single byte. It would be illegal to implement byte array updates on such a processor by simply reading an entire word, updating the appropriate byte, and then writing the entire word back to memory. This problem is sometimes known as word tearing, and on processors that cannot easily update a single byte in isolation some other approach will be required.

换句话说,这取决于 JVM 是否正确.

In other words, it's up to the JVM to get it right.

这篇关于java原始整数是设计的还是偶然的原子?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆