Java - 易失性不能按预期工作

时间:2016-04-23 18:03:53

标签: java

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

  

通过声明计数器变量volatile,对计数器变量的所有写操作都将立即写回主存储器。此外,计数器变量的所有读取都将直接从主存储器中读取。以下是计数器变量的volatile声明如何看起来

  

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

来源:tutorials.jenkov.com | Java Concurrency - Java Volatile Keyword

这使我得出结论/假设我对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();
    }

}

Main Class

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 : -2314, x : -1692, y : -1416, z : -1656)

我期待的是

(w : 0, x : 0, y : 0, z : 0)

或至少

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

问题

  1. 我认为/得出的错误导致了不同于预期的输出?
  2. 我的测试方法不正确吗?如果是的话,我该如何解决?
  3. 可选)您推荐有关于Java Concurrency的优秀教程吗?
  4. 备注

    • 我确实试图阅读类似的问题,但我无法完全理解提问者为了理解他的问题而试图做的事情。

1 个答案:

答案 0 :(得分:3)

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

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

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

  

(可选)你有没有关于Java Concurrency的好教程   建议?

通常被推荐作为该主题的权威处理的书是Brian Goetz的“Java Concurrency in Practice”。它有点旧了。