• JVM对高并发的支持-volatile


    volatile解决可见性与重排序的问题

    volatile是JVM提供的一个轻量级的同步机制

    1. 保证可见性

    volatile修饰的变量可以对所有线程立即可见

    JMM相关知识可知,当给变量加了volatile关键字后,当主内存中的变量被修改后,其他拷贝主内存中变量的线程会将之前拷贝的变量置为无效,重新从主内存中读取更新后的变量。

    2. 禁止指令重排

    java中,并非所有的语句都是原子性的(并非写成一行的语句就会原子执行)

    JVM会对编写的代码进行一些额外的优化,但是原则是:不影响单线程程序的执行结果,即最后的执行结果肯定是保证相同的。

    从一个单例模式开始说起

    public class Singleton {
       private static Singleton instance = null;//多个线程共享instance
       private Singleton() {}
       public static Singleton getInstance() {
           if (instance == null){
               synchronized(Singleton.class){
                  if (instance == null)
                      instance = new Singleton();
                   }
             }
             return instance;
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    ps:这段代码有问题,要不然不会总结volatile关键字

    instance = new Singleton();
    
    • 1

    上面这行代码不是一个原子性的操作,JVM在执行时大致拆分为以下三步:

    1. 分配内存地址、内存空间
    2. 实例化对象
    3. 将对象赋值为1分配好的内存

    由于重排序,可能是1、3、2,单线程情况下并不会出异常,但是多线程:

    线程X:执行到3(已被赋值了,不为null但是还未实例化),与此同时

    线程Y:执行到

    if (instance == null){
        
    }
    return instance;
    
    • 1
    • 2
    • 3
    • 4

    发现instance不为null ,直接将该未实例化的对象返回,后续如果使用的话会造成未知的错误

    ======================================================================================

    所以为了避免上述的问题,我们给instance加上volatile关键字,禁止JVM重排序

    private volatile static Singleton instance = null;
    
    • 1

    这样子,这个单例模式才算是完整

    注意:此处解决的问题为禁止重排序,但是volatile并不保证原子性,所以并不能保证线程安全

    volatile工作原理?

    内存屏障

    在volatile写操作前,插入一个StoreStore屏障

    在volatile写操作后,插入一个StoreLoad屏障

    在volatile读操作前,插入一个LoadLoad屏障

    在volatile读操作后,插入一个LoadStore屏障

    注意:volatile并不是线程安全的

    比如常见的“漏加”例子:

    public class TestVolatile_1 {
    	public static volatile int num = 0;
    	public static void main(String[] args) throws Exception {
    		for (int i = 0; i <100; i++) {
    			new Thread(new Runnable() {
    				@Override
    				public void run() {
    					for (int i = 0; i <20000; i++) {
    						 num++;//num++不是一个原子性操作
    					}
    				}
    			}).start();
    		}
    		Thread.sleep(3000);//休眠3秒,确保创建的100个线程都已执行完毕
    		System.out.println(num);
    	 }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    num++被多个线程同时执行,造成漏加

    可以使用JUC中的原子类来保证线程安全

    public class TestVolatile_2 {
    	public static AtomicInteger  num = new AtomicInteger(0);
    	public static void main(String[] args) throws Exception {
    	   for (int i = 0; i <100; i++) {
    		  new Thread(new Runnable() {
    			 @Override
    			 public void run() {
    				for (int i = 0; i <20000; i++) {
    				  num.incrementAndGet() ;// num自增,功能上相当于int类型的num++操作
    				}
    			 }
    		   }).start();
    		}
    	   Thread.sleep(3000);//休眠3秒,确保创建的100个线程都已执行完毕
    	   System.out.println(num);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    AtomicInteger实现了CAS算法

  • 相关阅读:
    物联网安全问题
    JS | 函数高级之原型、原型链
    MyBatis多条件查询、动态SQL、多表操作、注解开发详细教程
    [C++] STL :stack&&queue详解 及 模拟实现
    基于Java毕业设计在线商城系统源码+系统+mysql+lw文档+部署软件
    Elasticsearch—(MacOs)
    PHP木马原文
    计算机组成原理习题课第三章-2(唐朔飞)
    设计模式-19-State模式(状态模式)
    [Acwing-SpringBoot] Java语法基础入门
  • 原文地址:https://blog.csdn.net/Lionel_SSL/article/details/126824646