可重入锁- ReentrantLock

本章只分析 ReentrantLock,不分析 AQS 原理,AQS 从入门到精通传送门,ReentrantLock 的话源码还是比较简单的

类继承

// Lock 接口定义了加锁、释放锁的方法
public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
}

类成员

类属性

// 就一个属性,Sync 是内部类继承自 AQS
private final Sync sync;

内部类

// 继承 AbstractQueuedSynchronizer
abstract static class Sync extends AbstractQueuedSynchronizer {
  
  	// 定义 lock 方法(ReentrantLock实现了Lock接口)
  	abstract void lock();
  
  	// 非公平方式获取锁(公平方式获取锁在子类 FairSync 里)
  	final boolean nonfairTryAcquire(int acquires){}
  
  	// 实现 AQS 释放锁的模板方法
		protected final boolean tryRelease(int releases) {}
  
  	// 当前线程是否持有锁
  	protected final boolean isHeldExclusively() { return getExclusiveOwnerThread() == Thread.currentThread(); }
  	
  	// 当前持有锁的线程是哪个
  	final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread(); }
  
  	// 当前线程重入次数(如果当前线程没获取锁返回0)
  	final int getHoldCount() { return isHeldExclusively() ? getState() : 0; }
}

/**
 * 非公平锁
 */
static final class NonfairSync extends Sync {

    // 实现 Sync 的 lock 方法
    final void lock() { }

    // 实现 AQS 的 tryAcquire(),获取锁,非公平方式获取
    protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
}

/**
 * 公平锁
 */
static final class FairSync extends Sync {

     // 实现 Sync 的 lock 方法
    final void lock() { }

    // 实现 AQS 的 tryAcquire(),获取锁,公平方式获取
    protected final boolean tryAcquire(int acquires) { }
}

类方法

构造方法

// 无参构造(默认非公平锁)
public ReentrantLock() {
    sync = new NonfairSync();
}

// 有参构造(true:公平锁;false:非公平锁)
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

// 获取锁(获取不到就线程入队并阻塞住)
public void lock() {
    sync.lock();
}

// 获取锁(获取不到不会入队)
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

// 获取锁(获取不到不会入队,带超时时间)
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

// 释放锁
public void unlock() {
    sync.release(1);
}

lock 加锁原理

非公平锁

ReentrantLock

// java.util.concurrent.locks.ReentrantLock.Sync#lock
public void lock() {
    sync.lock(); // 这是个抽象方法,具体由子类实现:FairSync(公平)、NonfairSync(非公平)
}

NonfairSync

// java.util.concurrent.locks.ReentrantLock.NonfairSync#lock
final void lock() {
    if (compareAndSetState(0, 1)) // 不讲武德,不看队列有没有线程在排队,而是直接尝试持有锁(插队)
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1); // 插队失败的处理
}

AbstractQueuedSynchronizer

// java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire
public final void acquire(int arg) { // 这是AQS提供的标准的流程
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 3 个方法,核心
        selfInterrupt();
}

addWaiter()acquireQueued() 是 AQS 提供的,这里不展开说

这里只看 tryAcquire 怎么实现的,源码如下:

// java.util.concurrent.locks.ReentrantLock.NonfairSync#tryAcquire
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

// java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState(); // 获取 state
    if (c == 0) { // 如果是 0,说明锁未被任何线程持有,尝试 CAS 修改 state
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) { // 如果不是 0,说明锁已经被其他线程持有,本来就该返回 false 了,但是可以重入
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

公平锁

公平锁和非公锁逻辑基本一致,就是在获取锁时多了一个条件 hasQueuedPredecessors(),除此之外都是一样的

hasQueuedPredecessors() 方法就是看当前是否有线程排队,如果没有才获取锁,严格恪守先进先出的特性

// java.util.concurrent.locks.ReentrantLock.FairSync#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;
}

// java.util.concurrent.locks.AbstractQueuedSynchronizer#hasQueuedPredecessors
public final boolean hasQueuedPredecessors() {
    Node t = tail;
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

unlock 释放锁原理

ReentrantLock

// java.util.concurrent.locks.ReentrantLock#unlock
public void unlock() {
    sync.release(1);
}

AbstractQueuedSynchronizer

// java.util.concurrent.locks.AbstractQueuedSynchronizer#release
public final boolean release(int arg) {
    if (tryRelease(arg)) { // 释放锁
        Node h = head; // 取出头结点
        if (h != null && h.waitStatus != 0) // 是否有线程再等待获取锁,如果有就需要唤醒
            unparkSuccessor(h); // AQS 提供,原来的头结点改为0后,唤醒线程,头结点会更新(头结点出队)
        return true;
    }
    return false;
}

h != null:头结点为空,说明链表还未初始化,也表明没有其他线程在等待锁,不用唤醒下一个节点

h.waitStatus != 0:如果有其他线程等待锁,AQS 线程入队时,会把前驱节点状态改为 -1
所以如果等于0,也就是没有后继节点,也不用唤醒下一个节点的线程

唤醒后期节点 unparkSuccessor() 是 AQS 提供的,这里不赘述,看下怎么释放锁的,源码如下:

// 这个方法还是比较简单的,就是把 state -1
// java.util.concurrent.locks.ReentrantLock.Sync#tryRelease
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;
}

使用示例

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();
    private int counter = 0;

    public void increment() {
        lock.lock();  // 获取锁
        try {
            counter++;
            System.out.println(Thread.currentThread().getName() + " 增加计数器到: " + counter);
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();  // 释放锁
        }
    }

    public void performTask() {
        // 尝试获取锁,如果锁不可用则立即返回
        if (lock.tryLock()) {
            try {
                System.out.println(Thread.currentThread().getName() + " 获取到锁并执行任务");
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println(Thread.currentThread().getName() + " 无法获取锁,执行其他操作");
        }
    }

    public static void main(String[] args) {
        ReentrantLockExample example = new ReentrantLockExample();

        Runnable task1 = () -> {
            for (int i = 0; i < 5; i++) {
                example.increment();
            }
        };

        Runnable task2 = () -> {
            for (int i = 0; i < 5; i++) {
                example.performTask();
            }
        };

        Thread thread1 = new Thread(task1, "线程-1");
        Thread thread2 = new Thread(task1, "线程-2");
        Thread thread3 = new Thread(task2, "线程-3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}
posted @ 2023-05-24 15:59  CyrusHuang  阅读(38)  评论(0)    收藏  举报