1.ReentrantLock和Synchronize的比较
Jdk中独占锁的实现除了使用关键字synchronized外,还可以使用ReentrantLock。虽然在性能上ReentrantLock和synchronized没有什么区别,但ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景。
公平锁:当锁可用时,在锁上等待的时间最长的线程将获得锁的使用权(讲究先到先得)。先被cpu时间片选中的线程先获得锁。大部分情况我们使用非公平锁,非公平锁的性能比公平锁的性能高,但是公平锁能够防止线程饥饿,某些情况会比较有用。
非公平锁:通过线程间的竞争获取锁的使用权。
ReentrantLock可响应中断:在死锁情况下,使用Synchronized会一直等待获取锁,造成程序无法继续运行。ReentrantLock获取锁时可以使用lockInterruptibly()方法,此方法线程可被外部中断,不必一直等待获取锁,解决死锁问题。
获取锁时限时等待:tryLock(time)指定时间内获取锁对象,成功返回true,失败返回false。
相同点:独占锁(同步)、可重入
不同点:ReentrantLock可以通过构造器实现公平锁和非公平锁。ReentrantLock可响应中断。ReentrantLock可限时获取锁对象。
2.同步方法:
调用lock.lock()方法的线程等价于获取了对象锁标记(对象监视器)。lock.unlock()释放锁对象。
3.Condition实现等待通知机制:
lock.newCondition()获取condition对象,并且一个lock对象可以创建多个Condition对象。在调用await()或signal()方法前需要先获取锁对象(lock.lock()),否则会抛出异常。并且线程的等待和通知需要针对于同一个condition。
object的wait()方法相当于condition中await();object的notify()方法相当于condition中的signal();object的notifyAll()方法相当于condition中的signalAll()。
注意:
当调用condition.await()方法时,线程自动释放掉当前持有的锁对象。当线程被唤醒时会自动获取到原有的锁对象。
关于condition的使用,可以参考经典案例:ArrayBlockingQueue
4.lock和unlock的实现逻辑
https://cloud.tencent.com/developer/article/1038499
CAS简述:
CAS(Compare And Swap),即比较并交换。它是一种乐观锁(synchronized可以看做是悲观锁)是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。CAS有效地说明了“我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。
在JAVA中,sun.misc.Unsafe 类提供了硬件级别的原子操作来实现这个CAS。 java.util.concurrent 包下的大量类都使用了这个 Unsafe.java 类的CAS操作。
AQS简述:
AbstractQueuedSynchronizer 的缩写。AQS是一个用于构建锁和同步容器的框架。事实上concurrent包内许多类都是基于AQS构建,例如ReentrantLock,Semaphore,CountDownLatch,ReentrantReadWriteLock,FutureTask等。AQS解决了在实现同步容器时设计的大量细节问题。
AQS使用一个FIFO的队列表示排队等待锁的线程,队列中的每个节点都是双向链表的一环(有prev,next),头节点head不与任何节点关联,其他的节点都与等待线程关联,每个节点维护一个等待状态waitStatus。0表示正常状态,-1表示通知状态,表示该线程释放锁之后需要唤醒(通知)他的后继节点(儿子节点)。每当有一个新的线程newThread排进队列时,该节点newNode(newThread)自动成为尾部节点(tail)。如下是ReentrantLock的结构图:
AQS中的先进先出队列
AbstractQueuedSynchronizer中的lock方法
AbstractQueuedSynchronizer中的acquire方法
非公平锁获取锁对象nonfairTryAcquire
公平锁获取锁对象tryAcquire
添加等待队列节点addWaiter
挂起线程操作acquireQueued
锁的释放unlock
尝试释放锁对象tryRelease