ReentrantLock
java除了使用关键字synchronized外,还可以使用ReentrantLock实现独占锁的功能。
而且ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景。
- synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
- synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。
- synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。
ReentrantLock实现
ReentrantLock支持两种获取锁的方式,一种是公平模型,一种是非公平模型。
公平模型
- 初始化时, state=0,表示无人抢占锁。这时候A线程请求锁,占了锁,并把state+1。
- 线程B请求锁,线程B无法获取锁,生成节点进行排队。
- 线程A释放了一次锁,仅仅是把state-1。只有线程A把此锁全部释放了,状态值减到0了,其他线程才有机会获取锁。
- 当A把锁完全释放后,state恢复为0,然后会通知队列唤醒B线程节点,使B可以再次竞争锁。当一个线程节点被唤醒然后取得了锁,对应节点会从队列中删除。
- 如果B线程后面还有C线程,C线程继续休眠,除非B执行完了,通知了C线程。
非公平锁模型
- 初始化时, state=0,表示无人抢占锁。这时候A线程请求锁,占了锁,并把state+1。
- 线程B请求锁,线程B无法获取锁,生成节点进行排队。
- 线程A释放了一次锁,仅仅是把state-1。只有线程A把此锁全部释放了,状态值减到0了,其他线程才有机会获取锁。
- 当A把锁完全释放后,state恢复为0,然后会通知队列唤醒B线程节点,使B可以再次竞争锁。
- 如果在唤醒B线程节点的过程中,线程C请求锁,线程C可能获取到锁。
公平锁的lock方法在进行cas判断时多了一个hasQueuedPredecessors()方法,它会在AQS队列中没有中没有线程的情况下才会申请锁,而不像非公平锁一样,非公平锁一来不管AQS里是否有排队的线程就直接申请锁。