volatile 关键字没起到作用!

小灰狼 2020-10-07 05:05:50
public class SimpleVolatile1 {
static class BossThread {
public volatile int age = 30;

public void execBirth(){
age++;
System.out.println("boss.age++");
}

public void print(){
while(age < 31) {
System.out.println(age);
}
}
}

public static void main(String[] args){
BossThread bt = new BossThread();

for(int i=0; i<10; i++){
new Thread(bt::print).start();
}
new Thread(bt::execBirth).start();
}
}

在我的电脑上某次输出的结果是
30
30
30
30
boss.age++
30
30
30
30
30
30

按照 volatile 关键字的功能,当一个共享变量被标记为 volatile 时,它不应该会出现脏读现象,但我的测试程序里,boss.age++ 被打印出来之后,有些线程依然读到的是旧值!
有人能给解释一下吗!
谢谢!
...全文
3907 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq_39936465 2020-10-09
  • 打赏
  • 举报
回复
更正一下,不是不会同步是不会及时同步。而且各个pc 的实行速度问题,30并不一定只打印10,至少我的pc上会打印大于10次。
qq_39936465 2020-10-09
  • 打赏
  • 举报
回复
引用 10 楼 小灰狼 的回复:
[quote=引用 9 楼 新手村0617 的回复:]volatile只能保证可见性,不能保证原子性.
我没说要它会保证原子性 我是想写个程序来展示一下 volatile 对指令重排序的影响[/quote] 我觉得是你的程序有问题,你的线程共有是bt这个实例类,但是你的bt不会同步,造成每个线程的bt都是不相同的。
860MHz 2020-10-09
  • 打赏
  • 举报
回复
这个问题与volatile机制无关,主要在于System.out.println,源码如下: public void println(String x) { synchronized (this) { print(x); newLine(); } } 多线程并发时,需要抢夺PrintStream实例的对象锁。当一个线程打印时,其他线程的age值为30并且已经进入等待队列。如果此时age被改成31,并不会影响等待线程栈桢中的age的值。
小灰狼 2020-10-09
  • 打赏
  • 举报
回复
引用 9 楼 新手村0617 的回复:
volatile只能保证可见性,不能保证原子性.
我没说要它会保证原子性 我是想写个程序来展示一下 volatile 对指令重排序的影响
小灰狼 2020-10-09
  • 打赏
  • 举报
回复
引用 14 楼 mingwulipo 的回复:

package com.fr.function;

public class Test  {
    static class BossThread {
        public volatile int age = 30;

        public void execBirth(){
            age++;
            System.out.println("boss.age++");
        }

        public void print(){
            for (int i = 0; i < 10; i++) {
                System.out.println(age);
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        BossThread bt = new BossThread();

        new Thread(bt::print).start();
        Thread.sleep(50);
        new Thread(bt::execBirth).start();

    }
}

你的代码有问题,我改了下。 输出 30 30 30 30 boss.age++ 31 31 31 31 31 31
兄台想表达什么意思? 这样的代码,当然得出这样的结果!改过之后的代码,volatile 关键字有没有都没什么变化。
豆腐花发 2020-10-09
  • 打赏
  • 举报
回复

package com.fr.function;

public class Test  {
    static class BossThread {
        public volatile int age = 30;

        public void execBirth(){
            age++;
            System.out.println("boss.age++");
        }

        public void print(){
            for (int i = 0; i < 10; i++) {
                System.out.println(age);
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        BossThread bt = new BossThread();

        new Thread(bt::print).start();
        Thread.sleep(50);
        new Thread(bt::execBirth).start();

    }
}

你的代码有问题,我改了下。 输出 30 30 30 30 boss.age++ 31 31 31 31 31 31
qybao 2020-10-08
  • 打赏
  • 举报
回复
引用 3 楼 小灰狼 的回复:
明白你的意思
但是如此一来,那就是写不出体现 volatile 关键字不同的示例程序出来了?

怎么会写不了?
之前刚回答过一个类似的问题,参考该帖3L的回答和代码,加volatile和不加volatile分别运行程序看看效果。
https://bbs.csdn.net/topics/397699781
韩_师兄 2020-10-08
  • 打赏
  • 举报
回复
volatile只能保证可见性,不能保证原子性.
小灰狼 2020-10-08
  • 打赏
  • 举报
回复
引用 5 楼 qybao 的回复:
[quote=引用 3 楼 小灰狼 的回复:] 明白你的意思 但是如此一来,那就是写不出体现 volatile 关键字不同的示例程序出来了?
怎么会写不了? 之前刚回答过一个类似的问题,参考该帖3L的回答和代码,加volatile和不加volatile分别运行程序看看效果。 https://bbs.csdn.net/topics/397699781[/quote] 看过了这篇帖子,不过这里提供的示例是用来演示可见性的 但是我的目标是写个示例比较一下 volatile 关键字对指令重排带来的影响
小灰狼 2020-10-08
  • 打赏
  • 举报
回复
引用 6 楼 tianfang 的回复:
你的定义是:public volatile int age = 30; 它不是一个共享的静态变量,每个对象实例中,都有一个此变量 你在循环中启动了10个实例,打印了10个30,循环外的线程执行了execBirth,打印了boss.age++。你的执行结果都会是这样,只是boss.age++前后位置有少许不同 正确定义方式是:static volatile int age= 30; [quote=引用 4 楼 小灰狼 的回复:][quote=引用 2 楼 tianfang 的回复:] 你的定义是:public volatile int age = 30; 它不是一个共享的静态变量,每个对象实例中,都有一个此变量 你在循环中启动了10个实例,打印了10个30,循环外的线程执行了execBirth,打印了boss.age++。你的执行结果都会是这样,只是boss.age++前后位置有少许不同 正确定义方式是:static volatile int age= 30;
虽然 age 不是静态变更,但是每个线程引用的都是同一个对象。程序里只创建了一个 BossThread 对象,所以它还是共享的。[/quote] 你没有改变age,一直会是30,测试不出来[/quote] 没有规定要求 volatile 必须静态变量吧?至少我没看到文章或者文档这么说过!真要这样的话,Java编译器直接报错不就行了吗! 你再仔细看看我的示例,里面的 BossThread 并不是一个继承了 Thread 的类,我从头到尾都只创建了一个 BossThread 类型的对象 创建的线程是在 for 循环里,创建的线程对象引用、访问、操作的都是同一个 BossThread 对象,难道这不算是共享吗?
tianfang 2020-10-08
  • 打赏
  • 举报
回复
你的定义是:public volatile int age = 30; 它不是一个共享的静态变量,每个对象实例中,都有一个此变量 你在循环中启动了10个实例,打印了10个30,循环外的线程执行了execBirth,打印了boss.age++。你的执行结果都会是这样,只是boss.age++前后位置有少许不同 正确定义方式是:static volatile int age= 30;
引用 4 楼 小灰狼 的回复:
[quote=引用 2 楼 tianfang 的回复:] 你的定义是:public volatile int age = 30; 它不是一个共享的静态变量,每个对象实例中,都有一个此变量 你在循环中启动了10个实例,打印了10个30,循环外的线程执行了execBirth,打印了boss.age++。你的执行结果都会是这样,只是boss.age++前后位置有少许不同 正确定义方式是:static volatile int age= 30;
虽然 age 不是静态变更,但是每个线程引用的都是同一个对象。程序里只创建了一个 BossThread 对象,所以它还是共享的。[/quote] 你没有改变age,一直会是30,测试不出来
小灰狼 2020-10-07
  • 打赏
  • 举报
回复
引用 2 楼 tianfang 的回复:
你的定义是:public volatile int age = 30; 它不是一个共享的静态变量,每个对象实例中,都有一个此变量 你在循环中启动了10个实例,打印了10个30,循环外的线程执行了execBirth,打印了boss.age++。你的执行结果都会是这样,只是boss.age++前后位置有少许不同 正确定义方式是:static volatile int age= 30;
虽然 age 不是静态变更,但是每个线程引用的都是同一个对象。程序里只创建了一个 BossThread 对象,所以它还是共享的。
小灰狼 2020-10-07
  • 打赏
  • 举报
回复
引用 1 楼 qybao 的回复:
这是代码处理的原子性造成的。 假设print线程判断while(age<31)成立进入循环,刚想执行System.out.println(30)的时候(这里println的参数传递完毕,但是打印处理还没来得及执行的时候),cpu占用权被收回,轮到execBirth线程执行,execBirth线程执行完后,轮到print线程执行,这样print线程依然会打印30(之前println(30)参数都传好了,只是没来得及打印),所以最终就是你现在这样的结果。 所以,这个和volatile没关系,是代码处理的原子性造成的。
明白你的意思 但是如此一来,那就是写不出体现 volatile 关键字不同的示例程序出来了?
tianfang 2020-10-07
  • 打赏
  • 举报
回复
你的定义是:public volatile int age = 30; 它不是一个共享的静态变量,每个对象实例中,都有一个此变量 你在循环中启动了10个实例,打印了10个30,循环外的线程执行了execBirth,打印了boss.age++。你的执行结果都会是这样,只是boss.age++前后位置有少许不同 正确定义方式是:static volatile int age= 30;
qybao 2020-10-07
  • 打赏
  • 举报
回复
这是代码处理的原子性造成的。 假设print线程判断while(age<31)成立进入循环,刚想执行System.out.println(30)的时候(这里println的参数传递完毕,但是打印处理还没来得及执行的时候),cpu占用权被收回,轮到execBirth线程执行,execBirth线程执行完后,轮到print线程执行,这样print线程依然会打印30(之前println(30)参数都传好了,只是没来得及打印),所以最终就是你现在这样的结果。 所以,这个和volatile没关系,是代码处理的原子性造成的。

62,628

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧