Synchronized和volatile 面试简单汇总

synchronized

Synchronized的作用主要有三个:
        确保线程互斥的访问同步代码
        保证共享变量的修改能够及时可见
        有效解决重排序问题

从语法上讲,Synchronized总共有三种用法:
        修饰普通方法
        修饰静态方法
        修饰代码块


synchronized修饰普通方法时,使用的锁是对象的锁。
synchronized修饰静态方法时,使用的锁是类对象的锁Class。
synchronized修饰的代码块 使用的锁是对象锁。

监视者monitorenter


当对象被占用时就会变成锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
        如果monitor的进入数字为0,则该线程进入monitor,然后将进入数字设置为1,该线程即为monitor的所有者。
        如果线程已经占有该monitor,只是重新进入,则进入显示器的进入数加1。
        如果其他线程已经占用了monitor,则该线程进入双向状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

监控退出monitorexit

        执行monitorexit的线程必须是objectref所对应的monitor的所有者。
        指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被该monitor的线程可以尝试去获取这个monitor的所有权。

通过这两段描述,我们应该能很清楚的修剪Synchronized的实现原理,Synchronized的语义一致是通过一个monitor的对象来完成,实际上等待/通知等方法也依赖于monitor对象,这就是为什么只有在同步的块或方法中才能调用wait / notify等方法,否则会引发java.lang.IllegalMonitorStateException的异常的原因。
 

Synchronized底层实现方式

同步代码块

 

        synchronized映射成字节码指令就是增加来两个指令:monitorenter和monitorexit。当一条线程进行执行的遇到monitorenter指令的时候,它会去尝试获得锁,如果获得锁那么锁计数+1(为什么会加一呢,因为它是一个可重入锁,所以需要用这个锁计数判断锁的情况),如果没有获得锁,那么阻塞。当它遇到monitorexit的时候,锁计数器-1,当计数器为0,那么就释放锁。
        图上有2个monitorexit,synchronized锁释放有两种机制,一种就是执行完释放;另外一种就是发送异常,虚拟机释放。图中第二个monitorexit就是发生异常时执行的流程,就是“会有2个流程存在“。而且,从图中我们也可以看到在第13行,有一个goto指令,也就是说如果正常运行结束会跳转到19行执行。

同步方法

         方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。
        JVM就是根据该标示符来实现方法的同步的当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。
        在方法执行期间,其他任何线程都无法再获得同一个monitor对象。其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

synchronized与Lock的区别

类别

synchronized

Lock

存在层次

Java的关键字,在jvm层面上

是一个类

锁的释放

1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁

在finally中必须释放锁,不然容易造成线程死锁

锁的获取

假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待

分情况而定,Lock有多个锁获取的方式,大致就是可以尝试获得锁,线程可以不用一直等待

锁状态

无法判断

可以判断

锁类型

可重入 不可中断 非公平

可重入 可判断 可公平(两者皆可)

性能

少量同步

大量同步

wait()与sleep()的区别,简单来说wait()会释放对象锁而sleep()不会释放对象锁。

volatile

内存可见性
        Java 内存模型(JMM):在 Java 中所有的共享变量都在主内存中,每个线程都有自己的工作内存,为了提高线程的运行速度,每个线程的工作内存都会把主内存中的共享变量拷贝一份进行缓存,以此来提高运行效率。但这样就会产生一个新的问题,如果某个线程修改了共享变量的值,其他线程不知道此值被修改了,就会发生两个线程值不一致的情况,

        内存的可见性是指线程修改了变量的值之后,其他线程能立即知道此值发生了改变。
        volatile 只是轻量级的线程可见方式,并不是轻量级的同步方式,所以并不能说 volatile 是轻量级的 synchronized

内存屏障的3个功能:
        I. 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
        II. 它会强制将对缓存的修改操作立即写入主存;
        III. 如果是写操作,它会导致其他CPU中对应的缓存行无效。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值