ReentrantLock是lock接口的一个实现类,里面实现了可重入锁和公平锁非公平锁

ReentrantLock公平锁和不公平锁实现原理

公平锁会获取锁时会判断阻塞队列里是否有线程再等待,若有获取锁就会失败,并且会加入阻塞队列

非公平锁获取锁时不会判断阻塞队列是否有线程再等待,所以对于已经在等待的线程来说是不公平的,但如果是因为其它原因没有竞争到锁,它也会加入阻塞队列

进入阻塞队列的线程,竞争锁时都是公平的,应为队列为先进先出(FIFO)

默认实现的是非公平锁

public ReentrantLock() {
//非公平锁
sync = new NonfairSync();
}

还提供了另外一种方式,可传入一个boolean值,true时为公平锁,false时为非公平锁

//公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

非公平锁

非公平锁获取锁nonfairTryAcquire方法,对锁状态进行了判断,并没有把锁加入同步队列中

    static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L; final void lock() {
//比较当前状态 如果持有者是当前线程
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//如果不是 尝试获取锁
acquire(1);
} protected final boolean tryAcquire(int acquires) {
//获取锁
return nonfairTryAcquire(acquires);
}
} 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;
}

公平锁

代码和nonfairTryAcquire唯一的不同在于增加了hasQueuedPredecessors方法的判断

static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L; protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
int c = getState();
//判断当前对象是否被持有
if (c == 0) {
//如果等待队列为空 并且使用CAS获取锁成功 否则返回false然后从队列中获取节点
if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {
//把当前线程持有
setExclusiveOwnerThread(current);
return true;
}
}
//若被持有 判断锁是否是当前线程 可重入锁的关键代码
else if (current == getExclusiveOwnerThread()) {
//计数加1 返回
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//不是当前线程持有 执行
return false;
}
}

acquire()获取锁

    public final void acquire(int arg) {
//如果当前线程尝试获取锁失败并且 加入把当前线程加入了等待队列
if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//先中断当前线程
selfInterrupt();
}

关键代码

就是tryAcquire方法中hasQueuedPredecessors判断队列是否有其他节点

public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}

可重入性实现原理

在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功

由于锁会被获取n次,那么只有锁在被释放同样的n次之后,该锁才算是完全释放成功

1、获取锁方法

 protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
int c = getState();
//判断当前对象是否被持有
if (c == 0) {
//...略
}
//若被持有 判断锁是否是当前线程 可重入锁的关键代码
else if (current == getExclusiveOwnerThread()) {
//计数加1 返回
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//不是当前线程持有 执行
return false;
}

每次如果获取到的都是当前线程这里都会计数加1

释放锁

protected final boolean tryRelease(int releases) {
//每次释放都减1
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//等于0才释放锁成功
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}

如果锁被获取n次,也要释放了n次,只有完全释放才会返回false

最新文章

  1. Linux下设置Mysql数据库编码
  2. JQuery页面加载
  3. 动态规划(DP),模拟
  4. arcgis切图问题
  5. kafka集群配置与测试
  6. 给HttpClient添加Socks代理
  7. 使用kafka connect,将数据批量写到hdfs完整过程
  8. 使用centos 7安装conpot
  9. 强化学习6-MC与TD的比较-实战
  10. 关于 HTTP
  11. SQL语句创建数据库及表
  12. The &quot;Out of socket memory&quot; error
  13. 1.4激活函数-带隐层的神经网络tf实战
  14. Vue-Router路由Vue-CLI脚手架和模块化开发 之 使用props替代路由对象的方式获取参数
  15. 20155306 白皎 《网络攻防》Exp1 PC平台逆向破解——逆向与Bof基础
  16. HDUOJ----4502吉哥系列故事——临时工计划
  17. 通过SiteMapDataSource动态获取SiteMap文件进行权限设置
  18. [SQL] 理解SQL SERVER中的逻辑读,预读和物理读
  19. Android -- service 服务的创建与使用,生命周期,电话监控器
  20. 【整理】MySQL查询优化

热门文章

  1. HTTP系列之:HTTP中的cookies
  2. Mybatis-Plus - 条件构造器 QueryWrapper 的使用
  3. Jenkins 构建JOB失败
  4. Linux下cat命令的使用
  5. Redis的安装、基本使用以及与SpringBoot的整合
  6. 2021.9.12周六PAT甲级考试复盘与总结
  7. 这些解决 Bug 的套路,你都会了不?
  8. RocketMQ详解(三)启动运行原理
  9. css3 横屏
  10. python编码问题:UnicodeDecodeError: &#39;gbk&#39; codec can&#39;t decode byte 0xaf in position 68: illegal multibyte sequence