Synchronized面试总结
1. 说说自己对于synchronized关键字的了解
- Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法
- Synchronized的作用主要有三个
- 原子性:确保线程互斥的访问同步代码
- 可见性:保证共享变量的修改能够及时可见
- 有序性:有效解决重排序问题
- Synchronized总共有三种用法
- 当synchronized作用在实例方法时,监视器锁(monitor)便是对象实例(this)
- 当synchronized作用在静态方法时,监视器锁(monitor)便是对象的Class实例,因为Class数据存在于永久代,因此静态方法锁相当于该类的一个全局锁;
- 当synchronized作用在某一个对象实例时,监视器锁(monitor)便是某个对象实例
- synchronized 内置锁 是一种 对象锁,作用粒度是对象 ,是 可重入 的。其可重入最大的作用是避免死锁(子类同步方法调用了父类同步方法,如没有可重入的特性,则会发生死锁;)
2. synchronized关键字的底层原理?
- 任何一个对象都有一个Monitor与之关联,当对象的Monitor被持有后,该对象处于被锁定状态
- 当我们进⼊⼀个⽅法的时候,执⾏monitorenter,就会获取当前对象的⼀个所有权,这个时候monitor进⼊数为1,当前的这个线程就是这个monitor的owner。
- 如果你已经是这个monitor的owner了,你再次进⼊,就会把进⼊数+1
- 当执⾏完monitorexit,对应的进⼊数就-1,直到为0,才可以被其他线程持有
- 所有的互斥,其实在这⾥,就是看你能否获得monitor的所有权,⼀旦你成为owner,就是获得锁者。
3. Monitor底层原理
监视器可以确保监视器上的数据在同一时刻只会有一个线程在访问。
- Monitor是由ObjectMonitor实现的
- ObjectMonitor中有两个队列,_WaitSet 和 _EntryList
- 当多个线程同时访问一段同步代码时:首先会进入 _EntryList 集合,当线程获取到对象的monitor后,进入 _Owner区域并把monitor中的owner变量设置为当前线程,同时monitor中的计数器count加1;
- 若线程调用 wait() 方法,将释放当前持有的monitor,owner变量恢复为null,count自减1,同时该线程进入WaitSet集合中等待被唤醒;
- 若当前线程执行完毕,也将释放monitor(锁)并复位count的值,以便其他线程进入获取monitor(锁);
4. JDK1.6之后对synchronized关键字进行的优化?
JDK1.6版本为了弥补Synchronized的性能缺陷,设计了Synchronized锁的膨胀升级
- 无锁状态:当对象锁被创建出来时,在线程获得该对象锁之前,对象处于无锁状态。
- 偏向锁:在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,因此为了减少同一线程获取锁(会涉及到一些CAS操作,耗时)的代价而引入偏向锁。
- 轻量级锁:对于锁竞争比较激烈的场合,偏向锁就失效了,因为这样场合极有可能每次申请锁的线程都是不相同的,就升级为轻量级锁。
- 重量级锁:轻量级锁时,前对象锁被其他线程持有,于是该线程进行自旋,如果自旋失败到达阈值(默认是10),则将升级为重量级锁.
锁的膨胀升级,只能升不能降,也就是说升级过程不可逆。
5. 对象被创建的过程
6. Java对象头的组成
-
Mark Word(见下图):无锁状态下,Java对象头里的Mark Word里默认存储对象的HashCode、分代年龄和锁标记位。在64位的JVM中,Mark Word为64 bit。
-
Class Metadata Address:存储到对象类型数据的指针。
-
Array length:数组的长度(如果当前对象是数组)
Java对象头又存在于Java堆中,堆内存分为三部分:对象头,实例数据和对齐填充。
7. synchronized关键字与ReentrantLock的区别
- 都是可重入锁:自己可以再次获取自己的内部锁,同一线程每次获取锁,计数器加一,释放锁,计数器减一,计数为0,代表完全释放该锁
- synchronized依赖于JVM实现,ReentrantLock依赖于API
- 相比synchronized,ReentrantLock增加了一些高级功能
- 等待可中断 : ReentrantLock提供了一种能够中断等待锁的线程的机制,通过 lock.lockInterruptibly() 来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
- 可实现公平锁 : ReentrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。ReentrantLock默认情况是非公平的,可以通过 ReentrantLock类的ReentrantLock(boolean fair)构造方法来制定是否是公平的。
- 可实现选择性通知(锁可以绑定多个条件): synchronized关键字与wait()和notify()/notifyAll()方法相结合可以实现等待/通知机制。ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition()方法。