JDK之ReentrantLock

本文详细探讨了JDK中的ReentrantLock,包括其继承关系、内部类Sync、NonfairSync和FairSync的工作原理,以及加锁、释放锁的各种方法。ReentrantLock提供了公平性和非公平性的锁获取策略,其lockInterruptibly方法支持响应线程中断。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

怎么拥抱她终究要飞翔

1. 继承关系

在这里插入图片描述

  1. Serializable接口不必多说
  2. Lock接口六个方法:
public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

思考①:lockInterruptibly是何方妖孽?存在意义是什么?

顾名思义就是可中断的加锁。在加锁的过程中是可能会在队列中等待的,如果此过程不能被中断,那这个线程在成功加锁之前就无法被优雅结束了。所谓优雅,就是在结束之前能做一些资源释放之类的,而不是不计后果直接kill。

那可不可以用别的方法?肯定是有,但是大体思路和中断基本是一致的。详细有一个线程在等待队列中,现在我们要取消它,那做法有两种:

  1. 取队列里找到它,更新队列状态
  2. 更新线程的状态,等它执行的时候自己做判断

显然第一种实现起来麻烦很多,第二种就简单多了,而只要采用第二种思路,就会用一个类似标志位的东西,那思路就和中断异常一致了。

思考②:Condition是嘛?

这属于MESA模型的概念,借用《极客时间》王宝令老师的一张图,感兴趣的小伙伴可以自己去看一下
在这里插入图片描述

2. 内部类

2.1 Sync

继承AQS

abstract static class Sync extends AbstractQueuedSynchronizer{。。。}

非公平方式获取锁

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    // 获取同步器状态,也就是加载同步器上的锁的层数,如果是0,表示锁可用
    // 同步器概念约等于锁
    int c = getState(); 
    // 如果同步器状态可用,则尝试获取
    if (c == 0) {
    	// CAS获取锁
        if (compareAndSetState(0, acquires)) {
        	// 该方法属于AbstractOwnableSynchronizer定义的,就是设置同步器的当前
        	// 所属线程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 当同步器上的锁是当前线程加上的,也就是重入的情况
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        // 可重入锁最多可以加Integer.MAX_VALUE层
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

为什么说是非公平的?

该方法在判断同步器状态为可用时就立即以CAS方式尝试获取锁,但是此时可能等待队列中存在等待节点。


尝试释放锁

protected final boolean tryRelease(int releases) {
	// c为此次释放后,剩余的锁层数
    int c = getState() - releases;
    // 只有锁的持有线程才可以释放锁
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
    	// 锁彻底释放,可被再次获取
        free = true;
        // 清除锁的所属线程
        setExclusiveOwnerThread(null);
    }
    setState(c);
    // 只有锁彻底释放的时才是true
    return free;
}

其余一些方法

// 判断锁是否由当前线程持有
protected final boolean isHeldExclusively() {
    return getExclusiveOwnerThread() == Thread.currentThread();
}
// 返回与当前锁关联的condition对象
final ConditionObject newCondition() {
    return new ConditionObject();
}

final Thread getOwner() {
    return getState() == 0 ? null : getExclusiveOwnerThread();
}

final int getHoldCount() {
    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); // 重置锁状态
}

2.2 NonfairSync

继承Sync

static final class NonfairSync extends Sync {...}

加锁

final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}
  • 显然是非公平方式,先尝试获取
  • acquire方法是AQS里面,尝试获取锁失败后会加如等待队列
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

protected final boolean tryAcquire(int acquires) {
     return nonfairTryAcquire(acquires);
 }
  • nonfairTryAcquire就是Sync里面的方法

2.3 FairSync

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
		// 因为tryAcquire是公平的,所以acquire是公平的
        final void lock() {
            acquire(1);
        }

        // 公平方式的tryAcquire
        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;
        }
    }

3. 构造方法

public ReentrantLock() {
    sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
  • 默认是非公平锁
  • 可以接受一个公平与否的参数

4. 加锁方法

Lock

public void lock() {
    sync.lock();
}
  • FairSyncLock直接调用的AQSacquire,因为其重写了tryAcquire,而AQSacquire方法的公平性可以依赖于重写的tryAcquire方法,所以这个方法还算是公平的
  • NonfairSyncLock方法在一开始就直接尝试获取锁了,稳稳的不公平

lockInterruptibly

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}
  • AQS中提供了实现,如果线程被中断,则会抛出中断异常,否则会多次自旋尝试获取锁,未果后加入等待队列

tryLock

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}
  • tryLock无参都是不公平的

tryLock(long timeout, TimeUnit unit)

public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
  • 带时间参数的tryLock方法公平性受tryAcquire的影响

5. 释放锁方法

unlock

public void unlock() {
    sync.release(1);
}
  • 调用的是AQSrelease方法,其中又调用了SynctryRelease方法

6. 其它方法

// 锁的层数,就是state的值
public int getHoldCount() {
    return sync.getHoldCount();
}

// 所属线程
protected Thread getOwner() {
        return sync.getOwner();
    }
    
// 是否是公平锁
public final boolean isFair() {
        return sync instanceof FairSync;
    }

// ~
public String toString() {
        Thread o = sync.getOwner();
        return super.toString() + ((o == null) ?
                                   "[Unlocked]" :
                                   "[Locked by thread " + o.getName() + "]");
    }

7. 特殊用途的方法


// 获取等待线程,但是并不是准确的,因为线程状态是动态的
// 这个方法的目的是让子类更方便去实现一些更具扩展性的方法
protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }
    
// 获取等待在给定condition上的线程,但是并不是准确的,因为线程状态是动态的
// 这个方法的目的是让子类更方便去实现一些更具扩展性的方法
 protected Collection<Thread> getWaitingThreads(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

// 返回等待线程池数的估计值,用于监控系统状态,而非线程同步
public final int getQueueLength() {
        return sync.getQueueLength();
    }

// 目标线程是否处于等待队列中,主要用于监控系统状态
public final boolean hasQueuedThread(Thread thread) {
        return sync.isQueued(thread);
    }
// 是否有线程处于等待队列中,主要用于监控系统状态
public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }

// 在目标condition上是否有等待线程,主要用于监控系统状态
public boolean hasWaiters(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

// 判断这个锁是否被某个线程持有,用于监控系统状态,并不是为了同步控制
 public boolean isLocked() {
        return sync.isLocked();
    }

// 锁是否由当前线程持有,主要用于debug和测试
public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山与长生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值