一、 ReentrantLock概念
ReentrantLock,是java并发编程中提供的一种高级同步机制。实现了lock接口和Serializable接口,基于AQS(AbstractQueuedSynchronizer)实现了锁的功能。是一种可重入的互斥锁,支持公平锁和非公平锁的选择。
二、 相关属性
ReentrantLock 的实现是基于内部的sync 的属性实现,而sync 继承了AbstractQueuedSynchronizer,其核心属性有以下部分。
1、 private volatile int state:线程获取锁时候需要将state从0改为1,state为0代表没有线程持有锁,state大于0 代表某个线程正持有这把锁,state的值代表重入次数。
2、 private transient volatile Node head: 同步列表的头部指向下一个线程封装成的Node对象
3、 private transient volatile Node tail: 同步列表尾部元素,后期竞争失败的元素的prev指向tail,tail赋值为新的node.
三、构造函数
1、 无参构造函数 生成非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
2、有参构造函数,根据传入值 生成公平锁或非公平锁
public ReentrantLock(boolean fair) {
//传入参数为true为公平锁 false 非公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
3、构造函数中的两个内部类(FairSync与NonFairSync)都继承了Sync,两个主要区别的两个方法lock()和tryAcquire() (两个的tryAcquire的逻辑基本是一致的)
FairSync的 lock()
final void lock() {
//直接进入 acquire
acquire(1);
}
NonFairSync的Lock
final void lock() {
//直接尝试修改state获取锁
//获取成功设置锁持有线程为当前线程
//获取失败进入 acquire
if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
Sync的 acquire()
public final void acquire(int arg) {
//tryAcquire 成功直接返回
//
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
//获取当前线程和state的值
final Thread current = Thread.currentThread();
int c = getState();
// state 为0 去竞争锁,设置锁拥有线程
if (c == 0) {
// hasQueuedPredecessors 这个是公平锁特有的
//判断当前线程是否需要排队,也就是同步队列是否存在线程排队,存在则需要排队,不存在,不需要排队直接尝试获取锁。
// 非公平锁直接竞争锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// state不为0 判断当前线程是否为锁拥有线程
// 是 锁重入
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
四、 常见方法
1、 lock方法 获取锁
public void lock() {
sync.lock();
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
//判断node的前一个元素是否是head
//如果是去获取锁,获取成功设置head为当前node
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//判断线程是否被挂起进入等待
if (shouldParkAfterFailedAcquire(p, node) &&
//挂起当前线程并检查线程是否被中断
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
// 取消等待
cancelAcquire(node);
}
}
private Node addWaiter(Node mode) {
//将线程封装成Node
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {
node.prev = pred;
//将新生成Node放在同步队列末尾
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
} 2、 tryLock 获取锁
tryLock 的源码说明它只会获取一次锁,获取成功返回true,获取失败直接返回false,不会进去等待队列
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
3、 tryLock(Long timeOut ,TimeUnit unit)获取锁,超时失败,不进入等待队列。
4、 unLock() 释放锁,并唤醒等待队列后续线程
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
//释放锁
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒后续线程
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
// 重入次数减少
int c = getState() - releases;
//判断线程是否拥有锁 不拥有用抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
//锁释放成功
free = true;
//设置锁拥有线程为null
setExclusiveOwnerThread(null);
}
//修改state值
setState(c);
return free;
}
5、 getHoldCount 获取当前线程保持此锁的次数。
6、 isHeldCurrentThread 判断当前线程是否是持有锁的线程。
7、 isLocked 锁是否已经被获取,判断state是否为0。
8、 getOwner 获取持有锁的线程。
五、 特点
1、 可重入:正如其名称,ReentrantLock 是一个可重入的锁,意味着持有锁的线程可以 多次获取该锁,而不会引发阻塞。
2、 手动释放:与synchronized 不同,ReentrantLock 需要手动调用lock或unlock 获取释放锁,更加灵活也更加容易忘记释放锁。
3、 公平锁与非公平锁
a) 公平锁按线程请求顺序获取锁,先到先得
b) 非公平锁:不保证锁的获取顺序,线程请求时候会先去竞争一次锁,新能可能更好。
4、 trylock():可以非阻塞模式下使用,使用一些场景
a) 非阻塞操作:当线程不需要等待锁释放时,可以使用tryLock方法。如果锁不可用,线程可以立即执行其他任务,而不是进入等待状态。
b) 资源竞争较小:在资源竞争较小的情况下,使用tryLock可以减少线程的等待时间,提高程序的响应速度。
c) 避免死锁:在某些情况下,使用tryLock可以避免因长时间等待锁而导致的死锁问题。
六、 使用注意事项
1、 必须手动释放锁:忘记释放锁会导致其他线程永远无法获取锁。因此,通常在 try 块中获取锁,在 finally 块中释放锁,以确保异常情况下锁也能被正确释放。
2、 避免死锁:当多个线程获取锁时候,容易出现死锁问题。为了避免这问题,可以采用锁超时机制,或者设计更合理的锁加载顺序。
3、 新能与公平性的平衡:非公平锁性能较好,但可能导致某些线程长时间无法获取锁。公平锁避免了线程锁竞争的问题,但每次加锁和解锁性能较差。
4、 避免锁竞争:过度使用锁可能导致线程竞争性能下降。在设计多线程应用时,尽量减少临界区的长度,避免不必要的锁。
210

被折叠的 条评论
为什么被折叠?



