ReentrantLock

ReentrantLock

ReentrantLock是Java中的一个可重入锁实现,它提供了和synchronized关键字类似的线程同步功能,但相比synchronized更加灵活和可控。ReentrantLock可以用于实现线程间的互斥访问,避免了线程间的竞争和死锁问题。

特性

ReentrantLock的主要特点包括:

可重入性:与synchronized类似,ReentrantLock可以支持同一个线程对同一个锁的重复获取,避免了死锁问题。
可中断性:ReentrantLock提供了lockInterruptibly()方法,可以在等待锁的过程中响应中断,避免了线程一直等待的问题。
公平性:ReentrantLock提供了公平锁和非公平锁两种模式,可以根据需求选择不同的模式。
条件变量:ReentrantLock提供了Condition接口,可以实现线程间的协作和通信。

使用ReentrantLock时,需要手动进行加锁和解锁操作,可以使用lock()方法获取锁,使用unlock()方法释放锁。为了避免锁的竞争和死锁问题,通常需要合理地设计锁的范围和粒度,避免锁的持有时间过长。同时,为了提高代码的可读性和可维护性,建议使用try-finally语句块对锁进行包装,确保锁的释放。例如:

ReentrantLock lock = new ReentrantLock();
try {
    lock.lock();
    // 进行需要同步的操作
} finally {
    lock.unlock();
}

源码分析

可重入

		@ReservedStackAccess
        final boolean nonfairTryAcquire(int acquires) {
        	//获取当前线程
            final Thread current = Thread.currentThread();
            //获取所状态 
            //state = 0 锁空闲
            //state > 0 锁占用
            //state < 0 可冲入次数超过上线
            int c = getState();
            //判断当前锁是否占用
            if (c == 0) {
            	// 这里通过 cas 将 state 字段从 0 变更为 1 也就完成了加锁
                if (compareAndSetState(0, acquires)) {
                	//设置持有所得线程伪当前线程
                    setExclusiveOwnerThread(current);
                    //锁占用成功
                    return true;
                }
            }
            // 如果锁以占用,则判断当前线程是否为持有这个锁的线程
            else if (current == getExclusiveOwnerThread()) {
            	//一般情况下acquires 都为 1 每次对 state 进行 +1操作
                int nextc = c + acquires;
                //state 为 int 类型,当超过 int 访问之后则会变为负数
                //也基于此 最大可重复次数为 int 最大值
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                //校验成功 更新 state 值
                setState(nextc);
                //返回成功获取锁
                return true;
            }
            //获取锁
            return false;
        }

公平锁&非公平锁

公平锁 与非公平所得主要区别在于尝试加锁时判断前端是否有队列在等待获取锁。
公平锁与非公平锁 都是可重入锁,可重入逻辑同上

	//非公平锁
	static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        @ReservedStackAccess
        final void lock() {
        	//不考虑是否有现成等待,直接使用 CAS 获取锁
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
        	// 解析如上 可重入锁逻辑
            return nonfairTryAcquire(acquires);
        }
    }

	//公平锁
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
        	//acquire 会调用 tryAcquire 方法尝试加锁
            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;
        }
    }
	// 队列头尾不相同, 且头的下一个节点为空或头的下一个节点为自己的时候 认为没有线程在等待加锁,可以获取锁
    public final boolean hasQueuedPredecessors() {
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
    }

初始化

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

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

不间断获取锁

    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
    //AbstractQueuedSynchronizer 中的方法
    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
      	//判断线程是否被中断
        if (Thread.interrupted())
            throw new InterruptedException();
        //如果线程无法获取锁,启动不间断获取锁
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }
    
    //AbstractQueuedSynchronizer 方法
    private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
        //在等待队列中添加一个独占模式的等待节点
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
        	//通过一个死循环不间断地获取锁
            for (;;) {
            	//获取上一个节点
                final Node p = node.predecessor();
                //如果上一个节点和当前节点相同,尝试获取锁
                if (p == head && tryAcquire(arg)) {
                	//如果锁占用成功,则添加到啊队列的头部
                    setHead(node);
                    //方式node的引用一直被持有导致无法 被 gc(可达性)
                    p.next = null; 
                    failed = false;
                    return;
                }
                //当本次所获取失败时判断是否需要将当前线程挂起 如需要 则挂起线程并抛出中断异常
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
        	//如果上面的循环退出仍未获取到锁则 则将当前节点从等待队列中移除。
            if (failed)
            	//防止内存泄露 不管占用成功还是失败,已经操作过了就应该从队列中删除
                cancelAcquire(node);
        }
    }

你可能感兴趣的:(java,LOCK,基础)