JUC并发编程---ReentrantLock源码分析

1. ReentrantLock

ReentrantLock从名字看 – 重入锁(和synchronized关键字一样),同个线程在重复获取锁的话,会自动获取锁,而不是等待。ReentrantLock中又包含了公平锁、非公平锁的概念。下面列出构造函数和常用方法。

public class ReentrantLock implements Lock, java.io.Serializable {
	private final Sync sync;
	// 无参构造函数默认使用非公平锁。
	public ReentrantLock() {
        sync = new NonfairSync();
    }
    // 可以指定非公平锁或者是公平锁
    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);
    }
    // Condition用来替代传统的Object的wait()、notify()实现线程间的协作
    // 使用方法就和synchronized配合Object一样,condition要在获取到锁的线程中
    public Condition newCondition() {
        return sync.newCondition();
    }
}

2. lock

2.1 公平锁

static final class FairSync extends Sync {
	final void lock() {
	    acquire(1);
	}
	
	// 调用AbstractQueuedSynchronizer的acquire尝试将state设置为1
	public final void acquire(int arg) {
		// 如果获取锁失败,且将此线程丢到队列尾部,阻塞直到获取锁
		// addWaiter将线程丢到队列尾部,并返回新node
	    if (!tryAcquire(arg) &&
	        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
	        selfInterrupt();
	}

	// 尝试获取锁
	protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        // 如果没有持有者,队列里面没有人等待
        // 则尝试修改 state 获取锁
        if (c == 0) {
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // 如果有持有者,且为当前线程,计数器加1
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        // 其它情况,获取锁失败
        return false;
    }
	// 下面两个也是AbstractQueuedSynchronizer的方法
	final boolean acquireQueued(final Node node, int arg) {
	    boolean failed = true;
	    try {
	        boolean interrupted = false;
	        // 不断尝试去获取锁
	        for (;;) {
	        	// 获取上个节点,如果上个节点是头节点,尝试给当前节点获取锁
	            final Node p = node.predecessor();
	            if (p == head && tryAcquire(arg)) {
	            	// 如果获取到锁,将当前节点设置为头节点
	            	// 并将node中thread的引用设置为null
	                setHead(node);
	                p.next = null; // help GC
	                failed = false;
	                return interrupted;
	            }
	            // 如果当前状态是SIGNAL,则需要获取失败后park线程
	            // parkAndCheckInterrupt方法调用LockSupport.park(this);
	            // 阻塞当前线程,结束的时候调用interrupted
	            if (shouldParkAfterFailedAcquire(p, node) &&
	                parkAndCheckInterrupt())
	                interrupted = true;
	        }
	    } finally {
	    	// node.predecessor()中判断当前节点的前面节点,如果为空则抛出空指针
	        if (failed)
	            cancelAcquire(node);
	    }
	}
	
	private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
		/**
		* waitStatus表示节点的等待状态
		*   1 CANCELLED:  等待超时或者线程中断,结点会被设置为取消状态,不能改变状态。此状态不会去竞争锁
		* 	-1 SIGNAL:     节点的继任者被阻塞了,当此节点release或cancel的时候需要通知
        *   -2 CONDITION:  节点在条件队列中,不会被用作同步队列节点,直到转移,状态会设置为0
        *   -3 PROPAGATE:  使用在共享模式头结点有可能牌处于这种状态,表示锁的下一次获取可以无条件传播
        *   0:          除以上状态的,会处于这种状态,如新节点
		*/
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
        	// pred为Signal,表示当前节点被阻塞了,需要等待通知,所以要进入park
            return true;
        if (ws > 0) {
        	// 如果当前节点前个节点是取消状态,不断往前获取到一个不是这个状态
        	// 并设置其为当前节点的前节点,用于清理掉中间的节点
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
        	// 将前个节点状态改为SIGNAL
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
}

总结:重复获取到锁,计数器加1。获取锁失败则放入节点尾部,并进入等待,直到成为头部节点获取锁。

2.2 非公平锁

static final class NonfairSync extends Sync {
    final void lock() {
    	// 直接尝试加锁(这里不管是不是队列在队列头部,直接进行竞争),如果成功则将当前线程设置为持有者
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
			// 如果不成功,则还是非公平的去尝试获取锁
            acquire(1);
    }
	// 尝试非公平的获取锁
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

abstract static class Sync extends AbstractQueuedSynchronizer {
    final boolean nonfairTryAcquire(int acquires) {
        // ......
        // 如果当前锁没有持有者
        if (c == 0) {
        	// 与FairSync的tryAcquire只这一个地方不一致
        	// 不用判断队列是否有等待的,直接尝试获取锁
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // 当前线程有持有者
        else if (current == getExclusiveOwnerThread()) {
            intnextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        // ......
    }
}

总结:非公平锁会直接尝试获取锁而不考虑是否队列中有线程(与其竞争)。如果获取失败了,会放入等待队列(队列中的还是会保证顺序获取锁)。

3. unlock

public void unlock() {
	sync.release(1);
}
// 
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
        	// LockSupport.unpark(s.thread); 去唤醒一个线程
            unparkSuccessor(h);
        return true;
    }
    return false;
}

来看看释放的流程,ReentrantLock.Sync.tryRelease(int releases)方法

protected final boolean tryRelease(int releases) {
	// 这里releases是unlock传过来的1,所以c应该是0
    int c = getState() - releases; 
    // 如果不是获取锁的线程调用就抛IllegalMonitorStateException异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // 下面将持有线程清空,并重置下同步状态
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

4. tryLock

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

由上面分析nonfairTryAcquire可知,tryLock会尝试获取锁,获取成功返回true,如果是同个线程重复获取会将state增加,如果获取失败,并不会将线程放入队列,所以tryLock不是个阻塞方法。

5. tryLock(long timeout, TimeUnit unit)

public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
	return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

由字面意思很好理解这个方法的作用,在指定时间内尝试获取锁,如果没有获取到则返回false。

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer {

	public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
	    if (Thread.interrupted())
	        throw new InterruptedException();
	    // 公平或非公平的获取锁
	    return tryAcquire(arg) ||
	        doAcquireNanos(arg, nanosTimeout);
	}
	// 这个方法和acquireQueued在for循环中代码有所不同
	private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
        // ......
        // 新建一个Node,并将其上一个指向队列末尾
		final Node node = addWaiter(Node.EXCLUSIVE);
        for (;;) {
            // ......
            // 如果时间超过了,返回失败
            if (nanosTimeout <= 0)
                return false;
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            long now = System.nanoTime();
            nanosTimeout -= now - lastTime;
            lastTime = now;
            if (Thread.interrupted())
                throw new InterruptedException();
        }
        // ......
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值