【Java学习记录】volatile与synchronized

一、首先谈谈JMM内存模型

JMM内存模型图

  • JMM内存模型规定了线程的工作机理:即所有的共享变量都存储在主内存,如果线程需要使用,则拿到主内存的副本,然后操作一番,再放到主内存里面

  • 提取关键字:JMM内存模型、共享变量、主内存,主内存副本

    • JMM内存模型:JMM决决定一个线程对共享变量的写入何时对对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写共享变量的副本。这个本地内存并不真实存在,只是JMM的抽象概念。

    • 共享变量:类的成员变量就是共享变量,存在于主内存之中

      public class Test01{
      	//没有加volatile关键字修饰
      	public String share; //成员变量,也是共享变量
      
      	public static void main(String[] args){
      			share = "测试";	
      	}
      }
      
    • 主内存:所有的线程所共享的,主要包括本地方法区(jdk1.8之后被元空间所替代)和堆

      JVM模型

    • 主内存副本:线程执行读/写共享变量时在本地内存中创建的副本

二、JMM下volatile的作用

上面已经了解了JMM的作用,那么volatile关键字到底起到什么作用呢?

public class Test2{
	//使用volatile保证内存可见性
	public volatile String share;
	public static void main(String[] args){
			share = "测试volatile";
	}
}
  • 作用一:保证内存可见性,使用volatile关键字修饰之后,在编译的时候会主动为变量增加标识:ACC_VOLATILE,这样一写多读的场景,可以保证每个线程读到的数据是写入的
  • 作用二:禁止指令重排,比如在对使用volatile修饰的变量进行写之前和之后会分别插入一个StoreStore屏障StoreLoad屏障,这样就可以禁止CPU对指令的重排序越过这些屏障

所以通俗的来说,使用volatile可以保证可见性和有序性,这样能够保证其它内存能拿到最新的变量。但是使用volatile有以下的缺点:

  • 缺点一:无法保证原子性,volatile保证的是单个读取操作是具有原子性的,比如:

    int volatile a = 0; //保证了原子性,单次操作
    a += 1; //无法保证原子性,不止一次
    // a += 1
    // a = a+1
    // a = 2
    //总共执行三次操作
    
  • 缺点二:使用volatile修饰变量后,对该变量的写操作速度会降低

三、synchronized 的作用

保证内存的原子性,可见性,有序性

  • 问题一:如何保证内存的原子性,可见性,有序性?

    答案就是上锁,synchronized可以用于修饰普通方法静态方法以及以代码块的形成出现。原理就是JVM通过进入、退出对象监视器来实现对方法,同步块的同步。编译之后在同步方法调用之前加入一个monitor.enter指令,在退出方法和异常处插入monitor.exit指令。在这两条指令没有执行完毕前,其他线程都不能进入,形成了排他性,从而保证了原子性,可见性,有序性。

  • 问题二:synchronized上的是什么锁?

    锁的类型为对象锁

    类型 代码实例 锁住的对象
    普通方法 synchronized void test() 当前对象
    静态方法 synchronized static void test() 锁的对象是当前类的Class对象
    同步块 void fun(){ synchronized(this)} 锁的是()中的对象
  • 问题三:synchronized锁的几种形式

    • 偏向锁:偏向某一个线程,主要是为了处理同一个线程多次获取同一个锁的情况,比如锁重入或者一个线程频繁操作一个线程安全的容器;但一旦出现线程之间竞争同一个锁,偏向锁就会撤销,升级为轻量级锁

    • 轻量级锁:基于CAS操作,线程使用CAS尝试获取锁失败后,进行一段时间的忙等,也就是自旋操作。尝试一段时间仍无法获取锁才会升级为重量级锁

    • 重量级锁:基于底层操作系统实现的,每次获取锁失败都会直接让线程挂起,会带来用户态内核态的切换,性能开销比较大

      打一个比方:大家在排队打饭,你有一个专属通道,叫做帅哥美女专属通道,只有你一个人可以自由的同行,这就叫偏向锁

      突然有一天,我来了,我也自诩帅哥,所以我盯上了你的通道,但是你还在打饭,然后我就抢过去和你一起打饭,但是这样效率比较低,所以阿姨没问我的时候,我就玩会手机等你,这就叫轻量级锁

      突然还有一天,我饿到不行,什么帅哥美女统统滚蛋,就我一个人先打饭,所有阿姨为我服务,给我服务完了再轮到你们,这就叫重量级锁

声明:仅作学习记录用途,部分内容来自已有博文

猜你喜欢

转载自blog.csdn.net/Bob7856/article/details/107562233