JAVA并发包(三):ReentrantLock

ReentrantLock也叫做可重入锁,就是拿到锁的线程可以多次获得锁,体现在代码层面就是可以多次调用lock成功。

一、基本结构

下面是重入锁基本代码结构了,Sync继承了AQS(可以点击查看之前的博客解读),然后加锁和解锁是迪调用Sync的方法。所以只要理解了AQS,那么掌握重入锁就是水到渠成的事情了。

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;
    abstract static class Sync extends AbstractQueuedSynchronizer {
		abstract void lock();
		
	}
    static final class FairSync extends Sync {}
    static final class NonfairSync extends Sync {}
    public void lock() {
        sync.lock();
    }
	public void unlock() {
        sync.release(1);
    }

二、加锁

1.公平锁

	static final class FairSync extends Sync {
        // 公平锁的加锁开始不会参与锁竞争,会加入到先进先出的队列排队获取锁
        final void lock() {
            acquire(1);
        }
    }

	// 这是AQS类里面的方法,在AQS的文章讲过
	public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

	// 这里只讲解tryAcquire方法,acquireQueued和addWaiter方法在AQS文章讲过
	protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 状态为0说明没有线程获得锁,则可以参与锁竞争
            if (c == 0) {
            	// 参与锁竞争,首先判断队列中是否有线程在排队,没有则直接锁竞争,调用CAS。
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    // 获得锁,则设置当前线程占有锁
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 这里就是锁能够重入的证明,如果当前线程获得了锁,则直接更改锁状态,并且返回true代表获得锁
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            // 如果最后没有获得锁,则返回false,进入队列排队
            return false;
        }      

2.非公平锁

非公平锁与公平锁在代码中的区别是多了个if else,它的非公平在于,每次调用lock时线程首先参与一次锁竞争,如果竞争失败再走公平锁的流程。

	final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
    }

三、解锁

公平锁和非公平锁的解锁方法都是同一个

	public void unlock() {
        sync.release(1);
    }

	public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
            	// 如果队列还有其他线程等待获取锁则唤醒后继的等待线程
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

	protected final boolean tryRelease(int releases) {
			// 锁状态计数器减数
            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;
    }

四、总结

  1. 获得锁的线程可以重复获取锁,但是锁计数器是int值,需要注意不要溢出了。
  2. 重入锁的挂起是使用了AQS的方法,里面是用了LockSupport的park方法。
  3. 重入锁有公平和非公平两种,默认是使用非公平锁。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值