Java-Volatile未按预期工作

Java-Volatile未按预期工作,java,Java,我目前正试图研究并发性,特别是“volatile”关键字 通过声明计数器变量为volatile,所有对计数器变量的写入都将立即写回主存。此外,计数器变量的所有读取都将直接从主存中读取。下面是计数器变量的volatile声明的外观 及 当线程写入易失性变量时,不只是易失性变量本身被写入主存。此外,线程在写入易失性变量之前更改的所有其他变量也会刷新到主内存中。当一个线程读取一个易失性变量时,它还将从主存中读取所有其他变量,这些变量与易失性变量一起刷新到主存中 资料来源: 这使我得出结论/假设我对易失

我目前正试图研究并发性,特别是“volatile”关键字

通过声明计数器变量为volatile,所有对计数器变量的写入都将立即写回主存。此外,计数器变量的所有读取都将直接从主存中读取。下面是计数器变量的volatile声明的外观

当线程写入易失性变量时,不只是易失性变量本身被写入主存。此外,线程在写入易失性变量之前更改的所有其他变量也会刷新到主内存中。当一个线程读取一个易失性变量时,它还将从主存中读取所有其他变量,这些变量与易失性变量一起刷新到主存中

资料来源:

这使我得出结论/假设我对易失性变量所做的任何更改都将始终对所有线程可见。所以,我编写了一个代码来测试它

TestClass

package org.personal.test1;

class TestClass {
    public static int w = 0;
    public static int x = 0;
    public static int y = 0;
    public static volatile int z = 0;
    private static final int ITERATIONS = 100000;


    public static void sooPlus(int indents) {
        for (int i = 0; i < TestClass.ITERATIONS; i++) {
            TestClass.w++;
            TestClass.x++;
            TestClass.y++;
            TestClass.z++;
        }
    }

    public static void sooMinus(int indents) {
        for (int i = 0; i < TestClass.ITERATIONS; i++) {
            TestClass.w--;
            TestClass.x--;
            TestClass.y--;
            TestClass.z--;
        }
    }


    public static synchronized String getVariableValues () {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("(");
        stringBuilder.append("w : "+TestClass.w+", ");
        stringBuilder.append("x : "+TestClass.x+", ");
        stringBuilder.append("y : "+TestClass.y+", ");
        stringBuilder.append("z : "+TestClass.z+")");
        return stringBuilder.toString();
    }

}
package org.personal.test1;

/**
 * <ol type="I">
 *     <li>
 *         <a href="http://tutorials.jenkov.com/java-concurrency/volatile.html">jenkov.com - Java Volatile Keyword</a>
 *     </li>
 * </ol>
 */
public class Main {

    public static void main(String[] args) {
        Main.call1();
    }

    private static void call1() {
        Main.test1();
    }

    private static void test1() {
        Thread thread1 = new Thread("Thread1") {
            @Override
            public void run() {
                TestClass.sooPlus(1);
            }
        };

        Thread thread2 = new Thread("Thread2") {
            @Override
            public void run() {
                TestClass.sooMinus(4);
            }
        };

        thread1.start();
        thread2.start();

        try {
            thread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(TestClass.getVariableValues());

    }
}
我得到的结果不是我所期望的

我得到的(不同)

我所期待的

(w : 0, x : 0, y : 0, z : 0)
或至少

(w : -2314, x : -1692, y : -1416, z : 0)
问题

(w : -2314, x : -1692, y : -1416, z : -1656)
  • 我认为/得出的错误是什么,导致了与预期不同的输出
  • 我的测试方法不正确吗?如果是,那么我如何修复它
  • (可选)是否有您推荐的关于Java并发性的好教程
  • 注释

    (w : -2314, x : -1692, y : -1416, z : -1656)
    
    • 我确实试图阅读类似的问题,但我无法完全理解提问者试图做什么来理解他的问题

    volatile关键字提供了一种弱形式的线程安全性。它保证可见性,但不保证原子性或互斥性

    • 易失性字段是否可能不是线程安全的?线程安全。写入非易失性
      double
      long
      值不是原子的,但写入易失性
      double
      long
      变量是原子的

    • 由所有易失性字段组成的类是否可能不是线程安全的?是,如果对字段的写入导致无效状态转换(由于缺少同步)

    (可选)您有没有关于Java并发性的好教程 推荐


    Brian Goetz的《实践中的Java并发性》一书通常被推荐为对这一主题的权威性论述。它有点过时了。

    volatile关键字提供了一种弱形式的线程安全性。它保证可见性,但不保证原子性或互斥性

    • 易失性字段是否可能不是线程安全的?线程安全。写入非易失性
      double
      long
      值不是原子的,但写入易失性
      double
      long
      变量是原子的

    • 由所有易失性字段组成的类是否可能不是线程安全的?是,如果对字段的写入导致无效状态转换(由于缺少同步)

    (可选)您有没有关于Java并发性的好教程 推荐


    Brian Goetz的《实践中的Java并发性》一书通常被推荐为对这一主题的权威性论述。它有点过时了。

    volatile关键字提供了一种弱形式的线程安全性。它保证可见性,但不保证原子性或互斥性。易失性字段是否可能不是线程安全的?是,如果对字段的写入不是原子的。由所有易失性字段组成的类是否可能不是线程安全的?是,如果对字段的写入导致无效状态转换(由于缺少同步)。他是你的朋友。请看我对类似问题的回答,volatile关键字提供了一种弱形式的线程安全性。它保证可见性,但不保证原子性或互斥性。易失性字段是否可能不是线程安全的?是,如果对字段的写入不是原子的。由所有易失性字段组成的类是否可能不是线程安全的?是,如果对字段的写入导致无效状态转换(由于缺少同步)。他是你的朋友。请看我对类似问题的回答,-1,对不起。这个答案大部分是正确的(特别是提到互斥——你应该详细说明一下),但是易失性写入保证是原子的(参见),这个答案把(错误的)相反的主张放在了前面和中心。我站在正确的立场上。答案修改了。谢谢;否决票撤回。不过,对于OP的示例代码来说,真正重要的一点是类似于
    TestClass.w--
    由一个读操作和一个完全独立的写操作组成,其结果是它实际上不必递减
    TestClass.w
    。(它相当于
    int tmp=TestClass.w;tmp--;TestClass.w=tmp;
    ,这意味着它可以取代/覆盖/放弃对
    TestClass.w
    的其他并发更改)-1,抱歉。这个答案大部分是正确的(特别是提到互斥——你应该详细说明一下),但是易失性写入保证是原子的(参见),这个答案把(错误的)相反的主张放在了前面和中心。我站在正确的立场上。答案修改了。谢谢;否决票撤回。不过,对于OP的示例代码来说,真正重要的一点是类似于
    TestClass.w--
    由一个读操作和一个完全独立的写操作组成,其结果是它实际上不必递减
    TestClass.w
    。(它相当于
    int tmp=TestClass.w;tmp--;TestClass.w=tmp;
    ,这意味着它可以取代/覆盖/放弃对
    TestClass.w
    的其他并发更改)