为什么我的volatile关键字没有体现出变量的可见性?

发布于 2022-09-11 21:32:48 字数 594 浏览 22 评论 0

public class TestVol {      
    public volatile static int i=0;
    
    public static void main(String[] args) throws InterruptedException {        
        System.out.println(TestVol.i);
        Thread t1=new Thread(new Task(TestVol.i));
        Thread t2=new Thread(new Task(TestVol.i));
        t1.start();
        t2.start();
        System.out.println(TestVol.i);
        Thread.sleep(12000);
        System.out.println(TestVol.i);
    }
}

代码就是这样很简单。类里面神声明一个变量i,创建两个线程,Task任务类的工作是判断如果i==0的话,就讲i=123,但是我主方法变量i的时候,i的值还是0,并没有被线程修改啊。不是说volatile修饰的变量是都是可见的嘛?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

流年里的时光 2022-09-18 21:32:48

int 不是引用传递的,线程修改的应该已经不是那个volatile 的 i 了。

===================================
先把这个输出搞清再说:https://onlinegdb.com/rJ0braKMH

class SUB {
    public int s;
}

public class Main
{
    static int k;
    static SUB sub;
    static void ChangeInt(int l){
        l = 2;
    }
    static void ChangeObj(SUB obj) {
        SUB tmp = new SUB();
        tmp.s = 10;
        obj = tmp;
    }
    static void changeIntInObj(SUB sub) {
        sub.s = 12;
    }
    public static void main(String[] args) {
        k = 1;
        sub = new SUB();
        sub.s = 100;
        ChangeInt(k); // 试图改变 k
        System.out.println(k); // 但是失败了
        ChangeObj(sub); // 试图改变 sub
        System.out.println(sub.s); // 但是也失败了
        changeIntInObj(sub); // 这样改变 sub
        System.out.println(sub.s); // 终于成功了
        System.out.println("Hello World");
    }
}

输出:

1                                                                                                                                                                                  
100                                                                                                                                                                                
12
Hello World

虽然没有看到你的 Task 是咋写的,但是像这样只把一个 int 传进去,如果 Task 里只操作这个 int 的话,是没有办法改变你的 TaskVol.i 的。

蓝戈者 2022-09-18 21:32:48

兄弟,首先你的这个事例并不能很好的验证volatile的内存可见性。因为计算机的速度实在是太快了,你仅用两个线程几乎不可能看到被volatile修饰的变量和不被volatile修饰的变量有什么不一样的表现。
关于volatile关键字的语义我想你应该记住和理解一下两点就够了:

  • 保证被volatile修饰的变量对所有其他的线程的可见性
  • 使用volatile修饰的变量禁止指令重排优化。

以下是根据你的代码稍作修改验证volatile

public class TestVol {
    
    public static volatile int i;
    
    public static void main(String[] args) throws InterruptedException {        
        System.out.println(TestVol.i);
         Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("开始循环");
                while(TestVol.i == 0) {
                }
                System.out.println("结束循环");
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                TestVol.i = 123;
            }
        });
        t1.start();
        //这是为了让线程1中的死循环先跑起来
        Thread.sleep(1000);
        t2.start();
        //这一步是为了让线程2中123刷入主内存
        Thread.sleep(1000);
        //主线程打印i=123, 但是你会发现没有volatile修饰的i,死循环停不下来,
        //有volatile修饰的i,循环几乎与1=123进入主内存同时停下来
        System.out.println(TestVol.i); //123
    }
}

以上代码你可以试着分别执行被volatile关键字修饰和不被volatile修饰。会看到不被volatile修饰的话,线程t1的死循环根本停不下来,这就说明了t2线程对变量i的修改,线程t1是不可见的。但是如果变量i被volatile修饰的话,t1中的死循环会立马停下来。

花落人断肠 2022-09-18 21:32:48

Java中函数参数都是按值传递, new Task(TestVol.i)会将TestVol.i的值复制一份, 交给Task对象. 实际上Task对象指向的i已经不是TestVol.i了.

老旧海报 2022-09-18 21:32:48

这里例子能否满足需求,因为这里有四步,不能保证可见性的,只能简单的推理一下

只有 num = index; 这一步有可见性

加上 before 和 normal 、sleep 总共四步,部分 before 可以看到有改变的 3 - before - Thread-44

@Slf4j
public class VolatileTest {

    private volatile static int num = 0;

    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 100; i++) {
            Thread thread1 = new Thread(new Task1(i));
            thread1.start();
        }
    }

    static class Task1 implements Runnable {

        int index = 0;

        Task1(int index) {
            this.index = index;
        }

        @Override
        public void run() {
            log.info("{} - before - {}", num, Thread.currentThread().getName());
            num = index;
            log.info("{} - normal - {}", num, Thread.currentThread().getName());
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("{} - sleep - {}", num, Thread.currentThread().getName());
        }

    }

}

部分log

10:01:36.576 [Thread-94] INFO com.dawn.java.jvm.keyword.VolatileTest - 0 - before - Thread-94
10:01:36.576 [Thread-97] INFO com.dawn.java.jvm.keyword.VolatileTest - 0 - before - Thread-97
10:01:36.588 [Thread-94] INFO com.dawn.java.jvm.keyword.VolatileTest - 94 - normal - Thread-94
10:01:36.588 [Thread-88] INFO com.dawn.java.jvm.keyword.VolatileTest - 91 - sleep - Thread-88
10:01:36.576 [Thread-96] INFO com.dawn.java.jvm.keyword.VolatileTest - 0 - before - Thread-96
10:01:36.588 [Thread-92] INFO com.dawn.java.jvm.keyword.VolatileTest - 92 - normal - Thread-92
10:01:36.576 [Thread-99] INFO com.dawn.java.jvm.keyword.VolatileTest - 0 - before - Thread-99
10:01:36.588 [Thread-99] INFO com.dawn.java.jvm.keyword.VolatileTest - 99 - normal - Thread-99
10:01:36.588 [Thread-97] INFO com.dawn.java.jvm.keyword.VolatileTest - 97 - normal - Thread-97
10:01:36.588 [Thread-96] INFO com.dawn.java.jvm.keyword.VolatileTest - 96 - normal - Thread-96
10:01:36.576 [Thread-98] INFO com.dawn.java.jvm.keyword.VolatileTest - 0 - before - Thread-98
10:01:36.576 [Thread-44] INFO com.dawn.java.jvm.keyword.VolatileTest - 3 - before - Thread-44
10:01:36.588 [Thread-98] INFO com.dawn.java.jvm.keyword.VolatileTest - 98 - normal - Thread-98
10:01:36.588 [Thread-44] INFO com.dawn.java.jvm.keyword.VolatileTest - 44 - normal - Thread-44
10:01:36.569 [Thread-4] INFO com.dawn.java.jvm.keyword.VolatileTest - 4 - normal - Thread-4
10:01:36.569 [Thread-34] INFO com.dawn.java.jvm.keyword.VolatileTest - 34 - normal - Thread-34
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文