Synchronized面试

1.Synchronized关键字最主要的三种使用方式总结

a)修饰实例方法,作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁

b)饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁 

c)修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码块之前要获得给定对象的锁,但是Synchronized(class)代码块上是给Class类加锁

2.Synchronized关键字底层原理总结

a)Synchronized同步语句块:使用的是monitorenter和monitorexit指令,其中一个是同步代码块开始位置,一个是同步代码块结束位置,当执行到monitorenter指令时,线程视图获取锁也就是获取monitor(monitor对象存在于每个Java对象的对象头中,这也就是为什么Java中任意对象可以作为锁的原因)

b)Synchronized修饰方法:没有monitorenter和monitorexit,取而代之的是ACC_SYNCHRONIZED标识,该标识知名了该方法是一个同步方法,JVM通过该ACC_SYNCHRONIZED访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用

3.JDK1.6之后的底层优化

    在 Java 早期版本中,synchronized 属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的。如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的 synchronized 效率低的原因。1.6之后引入了各种锁

1)偏向锁:偏向锁在无竞争的情况下会把整个同步都消除掉

2)轻量级锁:在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗,因为使用轻量级锁时,不需要申请互斥量,另外轻量级锁加锁和解锁都用到了CAS操作。

3)自旋锁和自适应自旋:一般线程持有锁的时间都不是太长,所以仅仅是为了这一点时间去挂起线程/恢复线程是得不偿失的。为了让一个线程等待,我们只需要让线程执行一个自循环(自旋),这项技术就叫自旋。自适应的自旋锁,自适应的自旋锁带来的改进就是:自旋的时间不在固定了,而是和前一次同一个锁上的自旋时间以及锁的拥有者的状态来决定

4)锁消除:虚拟机在编译器运行时,如果监测到共享数据不可能存在竞争,那么就执行锁消除,可以节省毫无意义的请求锁的时间

5)锁粗化

4. Synchronized和ReenTrantLock的对比

1)两者都是可重入锁

    可重入锁的概念是自己可以再次获取自己的内部锁,比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。同一个线程每次获取锁,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。

2)Synchronized依赖于JVM而ReenTrantLock依赖于API

    ynchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团队在 JDK1.6 为 synchronized 关键字进行了很多优化,但是这些优化都是在虚拟机层面实现的,并没有直接暴露给我们。ReenTrantLock 是 JDK 层面实现的(也就是 API 层面,需要 lock() 和 unlock 方法配合 try/finally 语句块来完成),所以我们可以通过查看它的源代码,来看它是如何实现的。

3)ReenTrantLock比Synchronized增加高级功能

    ReenTrantLock增加了:1)等待可中断。2)可实现公平锁。3)可实现选择性通知(可以绑定多个条件)

1)ReentrantLock提供了一种能够中断等待锁的线程机制,通过lock.lockInterruptibly()来实现,正在等待的线程可以选择放弃等待,改为处理其他事情

2)ReentrantLock可以指定公平锁还是非公平锁。而Synchronized只能是非公平锁。

3)synchronized关键字与wait()和notify/notifyAll()方法相结合可以实现等待/通知机制,ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition() 方法。Condition是JDK1.5之后才有的,它具有很好的灵活性,比如可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。 在使用notify/notifyAll()方法进行通知时,被通知的线程是由 JVM 选择的,用ReentrantLock类结合Condition实例可以实现“选择性通知” ,这个功能非常重要,而且是Condition接口默认提供的。而synchronized关键字就相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在它一个身上。如果执行notifyAll()方法的话就会通知所有处于等待状态的线程这样会造成很大的效率问题,而Condition实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值