ReentrantReadWriteLock
中写锁,都是独占锁。ReentrantReadWriteLock
中读锁是共享锁。ReentrantReadWriteLock
读写锁的获取:setState()
方法增加写状态。compareAndSetState()
方法实现写状态的CAS增加,并将当前线程设置为持有写锁的线程。compareAndSetState()
方法实现读状态的CAS增加tryAcquire()
或者tryAcquireShared()
方法支持独占式或者共享式的获取同步状态。fair
参数进行指定,默认是false
,即默认为非公平的获取锁。FairSync
同步器,非公平使用NoFairSync
同步器。public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。下面用示例代码来进行分析:
ReentrantReadWriteLock
的读锁是支持重进入的共享锁,写锁是支持重进入的独占锁。synchronized
关键字实现同步的基础是每个对象都是一个锁,它依靠对象头存储锁。synchronized
关键字设计的、级别从低到高的4种状态。Mark Word
,用于存储对象的hashCode、分代年龄、锁等信息。无锁
偏向锁
Mark Word
中,线程再次进入或退出同步块时,直接检查Mark Word
中是否存储指向当前线程的偏向锁。如果存储了,则直接进入或退出同步块。Mark Word
中时才执行一次CAS操作。0 + 01
);如果锁对象处于锁定状态,撤销偏向后恢复到轻量级锁的状态(00
)。-XX:+UseBiasedLocking
。可以通过-XX:-UseBiasedLocking
关闭偏向锁。轻量级锁
0 + 01
),JVM 首先在当前线程的栈帧中开辟一块叫做锁记录(Lock Record
)的空间,用于存储同步对象的Mark Word
的拷贝。这个拷贝加了一个前缀,叫Displaced Mark Word
。Mark Word
更新为指向Lock Record
的指针,并将Lock Record
里的owner指针指向同步对象的Mark Word
。Mark Word
中的标志位更新为00
,表示对象处于轻量级锁定状态。Mark Word
是否指向当前线程的栈帧。如果是,说明当前线程已经持有了该对象的锁,可以直接进入同步块继续执行;否则,说明存在多线程竞争锁。Lock Record
中的Displaced Mark Word
与对象中的Mark Word
进行替换。重量级锁
10
,Mark Word
中存储的是指向重量级锁的指针。CPU
时间等待一会,这个等待需要执行忙循环(自旋) 来实现。-XX:PreBlockSpin
来进行设置。CPU
时间。JDK1.4.2
中引入,默认是关闭的;在JDK1.6
中变成默认开启,并为了解决自旋锁中浪费CPU
资源的问题,而引入了自适应自旋锁。1. Java中的乐观锁与悲观锁
2. 一个线程怎么判断自己是否可以获得共享资源的锁?
lock()
方法会调用AQS中的模板方法,模板方法会调用AQS中的可重写方法。在可重写方法中,会通过判断同步状态,决定是否可以获取共享资源的锁。ReentrantLock
的公平和非公平获取锁:同步状态为0和不为0的情况。ReentrantReadWriteLock
的写锁获取: 同步状态不为0和为0的情况。ReentrantReadWriteLock
的读锁的获取: 不是持有写锁的线程,获取失败;其他情况获取成功。3. 共享资源state何时增加何时减少?对于synchronized和lock有什么区别?
state = 0
表示锁被释放。synchronized
和lock都支持重进入:lock依靠AQS中的同步状态,synchronized
依靠对象头中Mark Word的锁标志位处于可偏向状态(1 + 01
)。4. 对于++i操作,如何不使用锁进行同步,保证其线程安全?
5. CAS操作的实现机制?
6. 公平锁与非公平锁
synchronized
只支持非公平访问, ReentrantLock
和ReentrantReadWriteLock
支持公平与非公平访问。ReentrantLock
为例,讲解如何支持公平与非公平的:构造函数中指定fair参数,对应的AQS为FairSync
或者NonfairSync
,公平访问要求没有前驱节点。7. lock ,sychronized,volatile的区别
synchronized
的同步基础,如何实现同步。volatile
保证变量在线程间的可见性和禁止指令重排序。只适合运算的结果不依赖当前变量值得情况,无法解决线程同步。sychronized
和ReentrantLock
为例,二者的异同:实现、性能、是否可中断、公平性支持、是否可绑定多个对象。synchronized
由JVM支持;导致前者需要显式的获取、释放锁;后者由JVM隐式完成。synchronized
进行了很多的优化,目前二者的性能大致相同。tryLock
)、中断获取(lockinterruptibly
)、超时获取(带参数的tryLock),而synchronized
不支持。synchronized
依靠进入和退出monitor对象,实现同步方法或同步代码块;lock依靠AQS实现锁的获取和释放。synchronized
依靠Object的监视器方法wait()
、notify()/notifyAll()
,实现等待/通知模式;lock接口的实现类,依靠绑定的condition对象的await()
、signal()/signalAll()
,实现等待/通知模式。8. 谈一下Java中的锁
synchronized
关键字提供锁功能,JDK1.5及以后
提供依靠Lock及其实现类提供锁功能。synchronized
关键字:实现同步的基础,锁存储的位置、如何实现同步。[ 如何保证原子性、可见性、顺序性 ]ReentrantLock
的重进入、公平与非公平;ReentrantReadWriteLock
的写锁/读锁的获取。synchronized
与ReentrantLock
的比较与选择VS
偏向锁VS
轻量级锁VS
重量级锁,它们的含义、具体的实例等9. synchronized关键字和ReentrantLock
synchronized
的同步基础、锁存储的位置、如何实现同步;ReentrantLock
的特性重进入、公平与非公平10. 共享锁与独占锁
11. 单例模式如何解决高并发?
instance
定义为volatile
,使用synchronized
再次进行instance
为null
的判断。12. AQS和condition对象的关系
ConditionObject
是AQS的内部类,一个AQS不仅拥有自身的同步队列,还拥有 ConditionObject
的等待队列;而且,一个AQS可以绑定多个condition对象。await()
方法时,要求当前线程已经获取到锁。即该线程处于同步队列的头节点中。接着当前线程释放该并将自己封装成新的节点加入到等待队列中,进入等待状态;从AQS的角度看,调用await()
方法时,节点从同步队列的头节点移动到等待队列的尾节点。signal()
方法时,会唤醒处于等待队列中等待最久的节点,即等待队列的头节点;线程想从await()
方法返回,必须获取到锁,因此线程又会封装成同步节点,加入到同步队列中。从AQS的角度看,调用signal()
方法时,节点从等待队列的头节点移动到同步队列的尾节点。13. lock接口的实现
lock()
、unlock()
等方法。