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

Java-Volatile未按预期工作[重复]

梁丘招
2023-03-14

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

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

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

来源:tutorials.jenkov.com|Java并发-JavaVolatile关键字

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

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 : -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并发的好教程推荐吗?

笔记

  • 我确实试图阅读类似的问题,但我不能完全理解提问者试图做什么来理解他的问题。

共有1个答案

严稳
2023-03-14

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

>

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

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

布莱恩·戈茨(Brian Goetz)的《实践中的Java并发》(Java Concurrency in Practice)一书通常被推荐为对这一主题的权威性论述。它有点老了。

 类似资料:
  • 我有一个数组,我试图删除数据的基础上,但部分数据被拉出和值填充的地方。 大堆 现在我想提取这些数据 完全基于 更新的命令: 请暂时忽略括号。目前在我得到的输出中 控制器中的代码:

  • 我正在使用spring Roo并希望访问Controller类中的一个bean,该类在ApplicationContext.xml中具有以下配置: 配置类本身是: 在我的Controller中,我认为一个简单的Autowired注释应该可以完成这项工作 在启动过程中,spring在setSkipWeeks方法中打印消息。不幸的是,每当我在控制器中调用config.getSkipWeeks()时,它

  • 当我运行以下程序时,它只打印 然而,从Java 8的equalsIgnoreCase文档中我们发现: 如果以下至少一项为真,则两个字符c1和c2被视为相同的忽略情况: •对每个字符应用java.lang.character.ToUpperCase(char)方法会产生相同的结果 所以我的问题是为什么这个程序不打印 在这两种操作中,都使用了大写字符。

  • 我试图使用来传输我根据前面的问题设置的自定义标头。 我在文件中读到... 我的属性包括:

  • 我正在和selenium一起工作,刮一些数据。 有一个按钮在页面上,我正在点击说“Custom_Cols”。这个按钮为我打开了一个窗口,我可以在那里选择我的列。 我的问题是为什么新窗口上的元素不可见,即使我正在等待元素的可见。补充一下,我已经尝试增加延迟时间,但我还是会偶尔出现这个错误。 我的密码在这里