请谈谈你对volatile的理解?

标准答案

volatile是java虚拟机提供的轻量级的同步机制,它有三个特征:
1、保证可见性
2、不保证原子性
3、禁止指令重排

可见性

当一个线程从主内存共享变量拷贝到工作内存的副本变量发生改变时,要写会主内存,主内存要第一时间通知其他线程的情况称之为内存模型中的可见性。

例子:当不添加volatile关键字修饰的时候,主内存共享变量被修改,不能通知其他线程,程序会进入死循环,当添加volatile后,主内存变量被修改后,可以立即对其他线程可见。

/**
 * 未添加volatile关键字
 */
public class VolatileDemo {
    public static void main(String[] args) {
        System.out.println("测试不添加volatile关键字的情况下,没有可见性,程序会进入死循环!");
        MyDate myDate = new MyDate();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "读取主内存变量值为: " + myDate.num);
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myDate.add();
            System.out.println(Thread.currentThread().getName() + "写回主内存变量值为: " + myDate.num);
        }, "测试线程").start();

        while (0 == myDate.num) {
        }
        System.out.println(Thread.currentThread().getName() + "线程读取到的主线程变量值为: " + myDate.num);
    }
}
class MyDate {
    public int num = 0;
    public void add() {
        num++;
    }
}
/**
 * 添加volatile关键字
 */
public class VolatileDemo {
    public static void main(String[] args) {
        System.out.println("测试不添加volatile关键字的情况下,没有可见性,程序会进入死循环!");
        MyDate myDate = new MyDate();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "读取主内存变量值为: " + myDate.num);
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myDate.add();
            System.out.println(Thread.currentThread().getName() + "写回主内存变量值为: " + myDate.num);
        }, "测试线程").start();

        while (0 == myDate.num) {
        }
        System.out.println(Thread.currentThread().getName() + "线程读取到的主线程变量值为: " + myDate.num);
    }
}
class MyDate {
    public volatile int num = 0;
    public void add() {
        num++;
    }
}

不保证原子性

原子性:不可分割,完整性,也就是某个线程在做某个具体业务时,中间不可被加塞或分割,也要整体完整。
volatile关键字修改的属性不能够保证原子性操作,虽然添加volatile关键字后,主内存的属性值被修改后,可以立即对其他线程可见,但是在某些特定瞬间,主内存通知其他线程时,其他线程操作完数据会写主内存时,线程被挂起,唤醒后工作内存的属性值没有被改变,仍然写会原来的操作后的属性值,则就不能数据的保证原子性。
解决方式:使用java.util.concurrent.atomic包下的类型
例子:通过对比方式比较原子性

public class MyDate {
    public volatile int num = 0;
    public volatile AtomicInteger atomicInteger = new AtomicInteger();
    public void add() {
        num++;
    }
    public void addAtomic() {
        atomicInteger.getAndIncrement();
    }
}
/**
 * Volatile不保证原子性
 **/
public class VolatileDemo1 {

    public static void main(String[] args) {
        MyDate myDate = new MyDate();
        for (int i = 0; i < 50; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myDate.add();
                    myDate.addAtomic();
                }
            }).start();
        }

        //需要等待50个线程都计算完成后,再用main线程取得最终的结果值,看是多少?
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " 获取int类型 num的值为:" + myDate.num);
        System.out.println(Thread.currentThread().getName() + " 获取Atomic类型atomicInteger的值为 " + myDate.atomicInteger);
    }
}

禁止指令重排

volatile实现禁止指令重排优化,从而避免多线程环境下程序出现乱序执行的现象。

有序性
计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令操作进行重新排序,一般是分为3种情况:
1、编译器优化重排
2、指令并行重排
3、内存系统的重排

此时,我们来了解一个概念,内存屏障(Merory Barrier)又称为内存栅栏,是一个cpu的指令,他的作用有两个:
1、保证特定的操作的执行顺序
2、保证某些变量的内存可见性(就是利用这个特性实现了volatile的内存可见性)

由于编译器和处理器都能执行指令重排优化,如果在指令间插入一条Merory Barrier 则会告诉编译器和cpu,不管什么指令都不能和这条Merory Barrier 指令重排,也就是说通过插入内存屏障,禁止在内存屏障的前后的指令重排优化。内存屏障另外一个作业是强制刷出各种cpu缓存数据,因此cpu上的线程都能读取到这些数据的最新版本。

小结

volatile 是java虚拟机轻量级的同步机制,它基本上遵守了JMM的规范,主要是保证了可见性,禁止指令重排序,但是他不保证原子性。
通过JMM我们知道,各个线程对主内存中共享变量的操作都是各个线程拷贝到自己的工作内存进行的,操作后再将变量副本写回主内存。这就可能存在一个线程修改了共享变量的值,但是还未写回主内存时,另一个线程又对主内存中同一个共享变量进行操作,到那时此时第一个线程操作后的变量副本写回主内操作,对后面的线程不可见。这种工作内存与主内存同步延迟现象就是造成了可见性问题。

线程安全性如何获得保证:
1、工作内存与主内存同步延迟现象导致的可见性问题:
可以使用synchronized和volatile关键字解决,他们都可以使一个线程修改后的变量立即对其他线程可见。
2、对于指令重排的导致的可见性问题和有序性问题:
可以利用volatile关键字解决,因为volatile的另一个作业就是指令重排。

你可能感兴趣的:(JAVA,JVM,volatile)