首页 > 其他分享 >从双重校验锁进一步理解synchronized和volatile

从双重校验锁进一步理解synchronized和volatile

时间:2022-09-21 21:36:12浏览次数:46  
标签:Singleton synchronized 校验 uniqueInstance 线程 有序性 volatile

并发编程中的四个问题:
可见性、原子性、有序性、指令重排
对于synchronizedvolatile首先我们知道:
synchronized可以保证原子性、有序性、可见性;
volatile只能保证有序性和可见性,但是可以防止指令重排;
那这几个概念是什么意思呢?

什么是可见性?

问题:并发编程时,当一个线程对共享变量进行了修改,另外的线程并没能立即看到最新的值。
原因:本地内存缓存会保存数据副本,这就造成一个线程修改内存的变量后,其他的线程可能由于读取的是本地内存的缓存而造成了数据不一致。(就好像这个变量的改变其他线程看不到一样)
image
解决思路:对于这种变量,应该每次都去内存中读,而不是走缓存;volatile和synchronized都可以解决这个问题。

什么是原子性?

问题:对于num++这个语句,实际上分为三步执行;
1.获取num的值;
2.计算加一之后的值;
3.将新值赋给num;
在多线程中,这三步应该看作一个整体一块执行完,中间不应让另外的线程也执行这三个语句;否者就可能出现问题:
解决思路:在一个线程执行完该代码块之前,其他线程不能执行该代码块;synchronized可以解决这个问题。

什么是有序性?

保证线程串行地执行某个代码块,volatile和synchronized都可以解决这个问题。

什么是防止指令重排?

为了提升执行速度,计算机再执行代码的时候会对指令进行重新排序,并保证达到的最终效果是一样的(但是,但是,它只能保证在单线程没啥问题,当使用多线程时遇上指令重拍就可能出现问题了)
案例:双重校验锁(未禁止指令重排时)

public class Singleton {

    private static Singleton uniqueInstance;

    private Singleton() {
    }

    public  static Singleton getUniqueInstance() {
       //先判断对象是否已经实例过,没有实例化过才进入加锁代码
        if (uniqueInstance == null) {
            //类对象加锁
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

对于uniqueInstance = new Singleton();语句其实是分三步走的:

  1. uniqueInstance分配内存空间
  2. 初始化uniqueInstance
  3. uniqueInstance指向分配的内存地址

但是JVM进行指令重排后,执行过程可能就变成了1->3->2;在多线程环境下,就可能导致一个线程获得还没有初始化的实例。比如线程A执行了1和3,此时T2调用getUniqueInstance后发现uniqueInstance不为null,因此直接返回uniqueInstance给其他程序使用,但实际上uniqueInstance还没初始化呢,如果用的话肯定有问题。
解决:volatile关键字可以禁止指令重排,因此双重校验锁正确的写法应该是:

public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public  static Singleton getUniqueInstance() {
       //先判断对象是否已经实例过,没有实例化过才进入加锁代码
        if (uniqueInstance == null) {
            //类对象加锁
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

一些对比

关于volatilesynchronized的有序性需要说明一下,synchronized保证的是代码块之间的有序性,确保多个线程串行地执行代码块,而对于代码块内部的有序是不保证的(也就是不会禁止指令重排);

总结:
volatile:可见性、有序性、禁止指令重排
synchronized:原子性、可见性、有序性

标签:Singleton,synchronized,校验,uniqueInstance,线程,有序性,volatile
From: https://www.cnblogs.com/shiruyanhai/p/16717110.html

相关文章

  • Spring data Jpa 自定义hql分页,添加动态参数校验1
    一,配置好jpa环境直接上代码1,控制器  其中 pageNumber和pageSize是我们自己前端传,filter中以字符串方式包含所需要的参数2.server  用json将参数解......
  • Volatile的解析
    volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情。由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与......
  • synchronized锁详解
    synchronized的意义解决了Java共享内存模型带来的线程安全问题:如:两个线程对初始值为0的静态变量一个做自增,一个做自减,各做5000次,结果是0吗?(针对这个问......
  • 神奇的volatile
    什么是volatile?  打开google,百度一下,你就知道~  java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。......
  • 自定义校验器 - List属性值中某个字段的重复性校验
    packagecom.runachina.sc.designer.domain.validator;importcom.runachina.cloud.starter.base.exception.RestResultException;importcom.runachina.cloud.starter......
  • JSR303数字校验
    天空和我的中间,只剩下倾盆的思念简单校验使用示例:引入对应的校验依赖<dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring......
  • synchronized和volatile区别
    synchronized和volatile区别synochronizd和volatile关键字区别:volatile关键字解决的是变量在多个线程之间的可见性;而sychronized关键字解决的是多个线程之间访问共享资源......
  • vue 正则 密码校验
    password:[/*{validator:checkPassword,trigger:'blur'}*/{pattern:/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&,.*]).{8,19......
  • Java中数据同步-synchronized关键字与Mointor的使用
    场景Java中Thread类的常用API以及使用示例:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/126596884在上面的基础上,学习线程同步的相关概念。数据不一致......
  • Gitea 1.17.2 | 带来视觉提升、完善资源校验、加强安全性等42项优化
    Gitea1.17.2合并了42个PullRequest,现已正式发布,我们建议所有用户升级到此版本。您可以到阅读原文了解更详细的介绍。致谢:@zeripath为Gitea贡献了诸多安全补丁!......