持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情
继承关系
ReentrantLock继承了Lock接口,Lock接口中定义了lock和unlock相关的操作
public class ReentrantLock implements Lock, java.io.Serializable
类的内部类
内部类结构
逐步分析源码
Sync
abstract static class Sync extends AbstractQueuedSynchronizer {
// 序列号
private static final long serialVersionUID = -5179523762034025860L;
// 获取锁
abstract void lock();
// 非公平方式获取
final boolean nonfairTryAcquire(int acquires) {
// 当前线程
final Thread current = Thread.currentThread();
// 获取状态
int c = getState();
if (c == 0) { // 表示没有线程正在竞争该锁
if (compareAndSetState(0, acquires)) { // 比较并设置状态成功,状态0表示锁没有被占用
// 设置当前线程独占
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;
}
// 试图在共享模式下获取对象状态,此方法应该查询是否允许它在共享模式下获取对象状态,如果允许,则获取它
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;
// 已经释放,清空独占
setExclusiveOwnerThread(null);
}
// 设置标识
setState(c);
return free;
}
// 判断资源是否被当前线程占有
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
// 新生一个条件
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
// 返回资源的占用线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
// 返回状态
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
// 资源是否被占用
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
// 自定义反序列化逻辑
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
NonfairSync
NonfairSync类继承了Sync类,表示采用非公平策略获取锁
// 非公平锁
static final class NonfairSync extends Sync {
// 版本号
private static final long serialVersionUID = 7316153563782823691L;
// 获得锁
final void lock() {
if (compareAndSetState(0, 1)) // 比较并设置状态成功,状态0表示锁没有被占用
// 把当前线程设置独占了锁
setExclusiveOwnerThread(Thread.currentThread());
else // 锁已经被占用,或者set失败
// 以独占模式获取对象,忽略中断
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
lock方法中我们可以看到,每次调用lock,当前线程都会先去尝试是否能够改变共享变量的值,如果可以则加锁成功,如果失败,则加入CLH队列
FairSyn
FairSync类也继承了Sync类,表示采用公平策略获取锁
// 公平锁
static final class FairSync extends Sync {
// 版本序列化
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
// 以独占模式获取对象,忽略中断
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
// 尝试公平获取锁
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取状态
int c = getState();
if (c == 0) { // 状态为0
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) { // 不存在已经等待更久的线程并且比较并且设置状态成功
// 设置当前线程独占
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { // 状态不为0,即资源已经被线程占据
// 下一个状态
int nextc = c + acquires;
if (nextc < 0) // 超过了int的表示范围
throw new Error("Maximum lock count exceeded");
// 设置状态
setState(nextc);
return true;
}
return false;
}
}
当资源空闲时,会先判断sync队列是否有等待时间更长的线程,如果存在,则将该线程加入到等待队列的尾部,实现了公平获取原则。
如果资源被其他线程占用,则会添加到sync队列的尾部,而不会先去尝试进行资源的获取。
类的构造函数
ReentrantLock(),默认是采取非公平策略获取锁
public ReentrantLock() {
// 默认非公平策略
sync = new NonfairSync();
}
ReentrantLock(boolean) ,可以通过参数进行策略的切换选择
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
源码分析总结
ReentrantLock是继承与Lock接口,实现了lock接口中的一些方法,而内部类则是基于AQS模板进行类的扩展,实现方式有两种,公平锁和非公平锁,可以通过构造函数中的参数进行控制,默认是非公平锁。
自定义锁
参考ReentrantLock的实现方式,自定义一个锁
首先我们要创建一个类MyLock,并且实现Lock接口的部分方法
package com.example.demo.thread.selflock;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
public class MyLock implements Lock{
AtomicInteger state = new AtomicInteger();
Thread ownerThread = null;
LinkedBlockingDeque<Thread> waiters = new LinkedBlockingDeque<>();
@Override
public void lock() {
if(!tryLock()){
waiters.add(Thread.currentThread());
for(;;){
if(tryLock()){
waiters.poll();
return;
}else{
LockSupport.park();
}
}
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
if(state.get() == 0){
if(state.compareAndSet(0,1)){
ownerThread = Thread.currentThread();
return true;
}
}else if(ownerThread == Thread.currentThread()){
state.set(state.get() + 1);
return true;
}
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public void unlock() {
if(ownerThread != Thread.currentThread()){
throw new RuntimeException("非法调用---当前这个锁不属于你");
}
if(state.decrementAndGet() == 0){
ownerThread = null;
Thread waiterThread = waiters.peek();
LockSupport.unpark(waiterThread);
}
}
@Override
public Condition newCondition() {
return null;
}
}
基于AQS的实现原理,自定义一个简单规则,包括lock()、tryLock()、unlock()三个方法
测试用例如下
package com.example.demo.thread.selflock;
import java.util.Date;
public class LockDemo {
static MyLock lock = new MyLock();
public static void main(String[] args) throws InterruptedException {
lock.lock(); // 其他没有获取到锁 阻塞 卡住不动
System.out.println("主线程获取到锁 其他线程卡住不动 " + Thread.currentThread().getName() + new Date());
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
System.out.println(Thread.currentThread().getName() + "---子线程获取到锁---");
lock.lock();
System.out.println(Thread.currentThread().getName() + "---子线程第2次获取到锁---");
lock.unlock();
System.out.println(Thread.currentThread().getName() + "---子线程释放锁---");
lock.unlock();
System.out.println(Thread.currentThread().getName() + "---子线程第2次释放锁---");
}
});
thread.start();
// 主线程睡眠3s
Thread.sleep(3000L);
System.out.println("3s后主线程释放锁 其他线程可以开始抢锁 " + Thread.currentThread().getName() + new Date());
lock.unlock();
}
}
结果