ReentrantLock-
前言: 为什么叫可重入锁,简单来说就是一个线程可以重复获取锁,那么这种又是怎么实现的可重入锁,本质上来说是依赖AQS为基础来实现的,在AQS类里面有一个内部类Node 就是一个链表,里面存了 上一个下一个 第一 最后一个 这种节点对象,每个节点对象里面,都有一个Thread 表示他是哪个线程的Node 如果节点是head 那么就获取,那么这个过程其实就算是 伪过程了,下面的内容讲讲,介绍一下,可重入锁 这个类;
内部类Sync:
提供所有实现机制的同步器,两个类继承了该类,一个是同步锁,一个是非同步锁。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
主要是为非公平锁,提供快捷方法
**/
abstract void lock();
//非公平的方法获取锁,
@ReservedStackAccess
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取当前锁的状态
int c = getState();
//判断当前锁,是否无人暂用 0无人 1有一个锁 2有两个锁
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//判断当前锁是否是独有线程-如果是进入
else if (current == getExclusiveOwnerThread()) {
//当前的state + 1 基本上都是
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//设置status 会有节点,AQS有一个遍历节点来执行的
setState(nextc);
return true;
}
//有人占用锁,且节点里面的线程并不是自有线程
return false;
}
//尝试发布信息 也就是修改status 状态
//这个方法,其实就是将status-1 在这个类里面有一个解锁的方法,就是调用这个方法 然后传入1
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
//初始化状态
boolean free = false;
if (c == 0) {
//如果等于0 status 就证明没人用,这里状态就为tree
//这里体现了抽象类的文档规则,如果是free 就为true否则为false
free = true;
//设置线程持有该锁
setExclusiveOwnerThread(null);
}
//更新状态
setState(c);
return free;
}
//判断持有锁的线程是否是当前线程
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
//创建一个新的条件
final ConditionObject newCondition() {
return new ConditionObject();
}
//获取持有线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
//获取计数
final int getHoldCount() {
//如果是当前线程持有-那么获取status 否则直接返回0
return isHeldExclusively() ? getState() : 0;
}
//判断是否是锁的状态
final boolean isLocked() {
return getState() != 0;
}
//读对象
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); //重制当前锁
}
}
内部类非公平对象NonfairSync:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
@ReservedStackAccess
final void lock() {
//这里是一个cas方法,判断当前是否无锁-如果无锁设置为1
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//如果有锁就+1
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
//父类的非公平锁-实现
return nonfairTryAcquire(acquires);
}
}
//这个方法是AQS类里面的方法
@ReservedStackAccess
public final void acquire(int arg) {
//调用子类的方法 -&&获取排队(其实就是添加一个节点)
如果成功-中断
if (!tryAcquire(arg) &&
//尝试获取-知道持有该线程的节点=head
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//中断
selfInterrupt();
}
内部类公平锁:FairSync
其实公平锁要比非公平锁,来讲效率低一些,应为如果要是有人在排队,那么就直接 排队去了 所有人都在排队,所有人都在抢,抢到了中断,期间一直都是cas的过程 比较交换-效率自然比较慢了
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
//直接调用
acquire(1);
}
@ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//获取当前状态
int c = getState();
if (c == 0) {
//查看是否有前辈队列,如果没有才可以像非公平锁一样获取
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//有前辈队列
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:因为其默认创建就是非公平对象,如果想要使用公平锁-那么就在创建ReentrantLock(true) 传入boolean值 true 为公平锁 fasle为非公平锁
2:lockInterruptibly
允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException但是正常lock 不会抛出异常,只是设置一个中断状态其就是将中断状态设置为true
那么什么是中断,其实就是设置一个中断状态,如果调用到该线程的时候,是中断状态,没有被唤醒,那么将会被挂起park 如果这个方法被中断,那么其结果就是抛出异常InterruptedException
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
3:tryLock 这个其实就是如果被中断,那么就在多长时间内将会被唤醒,唤醒后继续进行循环调用
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}