当前位置: 首页 > 知识库问答 >
问题:

实现原子操作,因为java volatile保证发生在关系之前?

洪成济
2023-03-14

实现原子操作,因为java volatile保证发生在关系之前?

我之前读过关于volatile的报道:

如果线程A写入一个易挥发变量,线程B随后读取相同的易挥发变量,那么线程A在写入易挥发变量之前可见的所有变量,在线程B读取易挥发变量后也将可见。

现在,我有两个变量:

         static int m_x;
volatile static int m_y;

现在我有两个线程,一个只写给他们,先写给m_x,再写给m_y;另一个,只从他们那里读,先读m_y,然后,m_x。

我的问题是:写操作是原子的吗?读取操作是原子的吗?

在我看来,它们应该是原子的:

(1) 在写线程方面,在(Write-1)之后,它不会将缓存刷新到主内存,因为m_x不是易失性的,因此,读线程无法看到更新;在(Write-2)之后,它会将缓存刷新到主内存,因为m_y是易失性的;

(2) 在读线程的一侧,on(read-1),它将从主存更新其缓存,因为m_y是易失性的;在(Read-2)上,它不会从主存更新缓存,因为m_x不是易失性的。

由于以上两个原因,我认为读取线程应该始终观察两个变量的原子值。正当

public class test {
         static int m_x;
volatile static int m_y;

public static void main(String[] args) {
    // write
    new Thread() {
        public
        void run() {
            while(true) {
                int x = randInt(1, 1000000);
                int y = -x;
                m_x = x; // (Write-1)
                m_y = y; // (Write-2)
            }
        }
    }.start();

    // read
    new Thread() {
        public
        void run() {
            while(true) {
                int y = m_y; // (Read-1)
                int x = m_x; // (Read-2)

                int sum = y + x;
                if (sum != 0) {
                    System.out.println("R:sum=" + sum);
                    System.out.println("R:x=" + x);
                    System.out.println("R:y=" + y);
                    System.out.println("\n");
                }
            }
        }
    }.start();
}

public static int randInt(int Min, int Max) {
    return Min + (int)(Math.random() * ((Max - Min) + 1));
}

}

共有2个答案

逑衡
2023-03-14

你断言

"...(写-1)后,它不会刷新其缓存到主存,因为m_x不是易失性的"

而且

“…在(Read-2)上,它不会从主存更新缓存,因为m_x不是易失性的。”

事实上,无法判断缓存是否将被刷新(写入),或者缓存中是否存在值(读取)。JLS肯定不会让人失望

虽然您可能会在某些平台上观察到程序的一致行为,但JLS并不能保证这种行为。

江子石
2023-03-14

如注释中所述,这两个读写不是原子的。使用易失性关键字无法实现原子性。

这个事实可以在运行程序时观察到。

要同时读/写这两个变量,要么需要适当的同步,要么创建自己的不可变值。

以后再做

public class ConcurrencyTestApp {

    // use volatile for visibility
    private volatile ImmutableValue immutableValue = new ImmutableValue(0, 0); // initial, non-null value

    static class ImmutableValue {
        private final int x;
        private final int y;

        ImmutableValue(final int x, final int y) {
            this.x = x;
            this.y = y;
        }

        int getX() {
            return x;
        }

        int getY() {
            return y;
        }

        @Override
        public String toString() {
            return String.format("x = %s\t| y = %s", x, y);
        }
    }

    void replaceOldWithNewValue(final ImmutableValue newValue) {
        immutableValue = newValue;
    }

    ImmutableValue getImmutableValue() {
        return immutableValue;
    }

    static class Writer extends Thread {

        private final ConcurrencyTestApp app;

        Writer(ConcurrencyTestApp app) {
            this.app = app;
        }

        volatile boolean isRunning = true;

        @Override
        public void run() {
            while (isRunning) {
                int x = randInt(1, 1000000);
                int y = -x;

                app.replaceOldWithNewValue(new ImmutableValue(x, y));
            }

        }

        int randInt(int Min, int Max) {
            return Min + (int) (Math.random() * ((Max - Min) + 1));
        }
    }

    static class Reader extends Thread {
        private final ConcurrencyTestApp app;

        Reader(ConcurrencyTestApp app) {
            this.app = app;
        }

        volatile boolean isRunning = true;

        @Override
        public void run() {
            while (isRunning) {
                ImmutableValue value = app.getImmutableValue();
                System.out.println(value);
                int x = value.getX();
                int y = value.getY();
                int sum = x + y;
                if (sum != 0) {
                    System.out.println("R:sum=" + sum);
                    System.out.println("R:x=" + x);
                    System.out.println("R:y=" + y);
                    System.out.println("\n");
                }
            }
        }
    }

    public static void main(String[] args) {

        ConcurrencyTestApp app = new ConcurrencyTestApp();
        Writer w = new Writer(app);
        Reader r = new Reader(app);

        w.start();
        r.start();

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        w.isRunning = false;
        r.isRunning = false;
    }

}

为了进一步参考,我推荐Brian Goetz和Tim PeierlsJava实践中的并发这本书。

补遗

...

由于以上两个原因,我认为读取线程应该始终观察两个变量的原子值。正当

错误的

...你错过了一个重要的部分。

如需参考,请参阅Jeremy Manson和Brian Goetz的JSR 133(Java内存模型)常见问题解答部分volatile做什么?

在您的程序中,没有任何东西可以阻止以下操作:

假设int m_x=x1和int m_y=y1

  • 写入线程一直执行到Write-1
  • int y=m_y仍然是y1,因为您的Writer线程尚未进一步执行
  • int m_y现在被设置为值y2(只有现在Read-1才会得到y2,Read-2才会保证得到x2,除非你的Writer线程继续)

看到你自己修改你的作者

System.out.println("W0");
m_x = x; // non-volatile
System.out.println("W1: " + x);
m_y = y; // volatile
System.out.println("W2: " + y);

和读卡器线程代码

System.out.println("R0");
int y = m_y; // volatile
System.out.println("R1: " + y);
int x = m_x; // non-volatile
System.out.println("R2: " + x);

那为什么它对你不起作用呢?

参考文献

...无论是否易失性,当线程A写入易失性字段f时对其可见的任何内容在线程B读取f时对其可见。

因此,只有当写入线程将新值写入m_y时,你的读线程才能保证看到m_xm_y的新值。但是,由于没有特定的线程执行顺序得到保证,在执行Read-1之前,写操作write-2可能不会发生。

另请参阅Jakob JenkovJavaVolatile Keyword的类似示例。

 类似资料:
  • 我有一个关于Java中代码重新排序和竞争条件的问题。 假设我有以下代码,有2个或多个线程同时执行: JVM是否可能以错误的顺序执行此操作?例如,以下重新排序是否可能?: 还是保证锁不会被重新排序?

  • 更多面试题总结请看:【面试题】技术面试题汇总 malloc / free 简介 void *malloc(size_t size) void free(void *ptr) malloc 分配指定大小的内存空间,返回一个指向该空间的指针。大小以字节为单位。返回 void* 指针,需要强制类型转换后才能引用其中的值。 free 释放一个由 malloc 所分配的内存空间。ptr 指向一个要释放内存

  • 其中“url”是JSON格式的,它使用http GET方法返回大量数据。 我无法从我在getResultForRequest(url)方法中传递的“url”中获取JSON格式的数据。我在urlConnection.connect();中出错。Internet权限也在AndroidManifest.xml文件中给出。 这是我的日志。 提前谢谢。

  • 简介 在 DB 中 Swoft 提供了 select、selectOne、update、insert、delete、cursor、statement、affectingStatement、unprepared 方法进行原生操作。 新增数据 $bool = DB::insert('INSERT INTO users (`id`, `name`) VALUES (?, ?)', [1, 'Swoft'

  • 主要内容:常用的原子操作命令维护原子性的推荐方法是保留所有相关信息,并将这些信息使用嵌入式文档的形式更新到文档中,这将确保单个文档的所有更新都是原子的。假设我们已经创建了一个名为 productDetails 的集合,并在其中插入了一个文档,如下所示: 在上面的文档中,我们将购买产品的客户的信息嵌入到 product_bought_by 字段中。当有新客户购买该产品时,我们首先会使用 product_available 字段

  • python多进程共享一个可操作的变量, 如何保证原子操作? 我的需求 目前有多组数据需要执行计算型任务, 每执行完一组数据就要给java通知并传递生成的结果文件, 并且我要在把所有任务都执行完的时候(也就是最后一组数据执行完的时候)告诉java本轮次数据全部执行完毕, java那边就要去整体做数据库入库的操作。 我的设想 多进程之间维护一个整型数值, 在执行完一组数据的时候自增1, 并在通知ja