Java volatile之println细节

volatile定义

volatile是java虚拟机提供的轻量级的同步机制

特性

保证可见性

不保证原子性

禁止指令重排

示例

在不使用volatile的情况下,主线程获取的num和子线程中设置数值的num存放在不同位置的,彼此不可见,因此,在子线程中设置num的值时,主线程中的num并不会被改变,依然是num=0

public class VolatileTest {
    public static void main(String[] args) {
        MyData myData = new MyData();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+" 修改前的值:"+myData.num);
            //休眠
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //修改数值
            myData.setNum();
            System.out.println("修改后的值:"+myData.num);
        }).start();

        while (myData.num == 0){

        }
        System.out.println(Thread.currentThread().getName()+"执行完");
    }
}
class MyData{
    int num = 0;
    public synchronized void setNum() {
        num = 123;
    }
}

运行结果:
Thread-0 修改前的值:0
修改后的值:123
(循环)

但是加了volatile之后,使num成员变量存储在了内存中,增加了num的可见性,因此使得主线程能够访问到子线程设置的num,从而能退出循环,继续执行

public class VolatileTest {
    public static void main(String[] args) {
        MyData myData = new MyData();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+" 修改前的值:"+myData.num);
            //休眠
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //修改数值
            myData.setNum();
            System.out.println("修改后的值:"+myData.num);
        }).start();

        while (myData.num == 0){

        }
        System.out.println(Thread.currentThread().getName()+"执行完");
    }
}
class MyData{
    volatile int num = 0;
    public synchronized void setNum() {
        num = 123;
    }
}

运行结果:

Thread-0 修改前的值:0
修改后的值:123
main执行完

But

如果在while循环中加入了输出语句,结果就不一样了

以不加volatile为例

public class VolatileTest {
    public static void main(String[] args) {
        MyData myData = new MyData();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+" 修改前的值:"+myData.num);
            //休眠
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //修改数值
            myData.setNum();
            System.out.println("修改后的值:"+myData.num);
        }).start();

        while (myData.num == 0){
            System.out.println("正在循环");
        }
        System.out.println(Thread.currentThread().getName()+"执行完");
    }
}
class MyData{
    int num = 0;
    public synchronized void setNum() {
        num = 123;
    }
}

运行结果:
...
正在循环
正在循环
修改后的值:123
main执行完

此时会发现在不加volatile的情况下,程序竟然结束了循环!也就是说,主线程在经过了1秒后竟然能够访问子线程中修改后的num值了,而不是之前的死循环。

这又是为什么呢?

通过查看System.out.println(); 这个输出语句的底层源码可见

    /**
     * Prints a String and then terminate the line.  This method behaves as
     * though it invokes <code>{@link #print(String)}</code> and then
     * <code>{@link #println()}</code>.
     *
     * @param x  The <code>String</code> to be printed.
     */
    public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

好家伙,直接在println方法中同步了PrintStream 对象,当我们调用输出方法时,主线程和子线程中的num值得到了同步,使得产生了一种,好像不用volatile也能实现主线程访问子线程修改后的num的感觉。

因此在此得到了解释。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值