死磕 java同步系列之ReentrantLock源码解析(一)——公平锁、非公平锁 (3)

以上就是整个公平锁获取锁的过程,下面我们看看非公平锁是怎么获取锁的。

非公平锁 // ReentrantLock.lock() public void lock() { sync.lock(); } // ReentrantLock.NonfairSync.lock() // 这个方法在公平锁模式下是直接调用的acquire(1); final void lock() { // 直接尝试CAS更新状态变量 if (compareAndSetState(0, 1)) // 如果更新成功,说明获取到锁,把当前线程设为独占线程 setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } // ReentrantLock.NonfairSync.tryAcquire() protected final boolean tryAcquire(int acquires) { // 调用父类的方法 return nonfairTryAcquire(acquires); } // ReentrantLock.Sync.nonfairTryAcquire() final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 如果状态变量的值为0,再次尝试CAS更新状态变量的值 // 相对于公平锁模式少了!hasQueuedPredecessors()条件 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }

相对于公平锁,非公平锁加锁的过程主要有两点不同:

(1)一开始就尝试CAS更新状态变量state的值,如果成功了就获取到锁了;

(2)在tryAcquire()的时候没有检查是否前面有排队的线程,直接上去获取锁才不管别人有没有排队呢;

总的来说,相对于公平锁,非公平锁在一开始就多了两次直接尝试获取锁的过程。

lockInterruptibly()方法

支持线程中断,它与lock()方法的主要区别在于lockInterruptibly()获取锁的时候如果线程中断了,会抛出一个异常,而lock()不会管线程是否中断都会一直尝试获取锁,获取锁之后把自己标记为已中断,继续执行自己的逻辑,后面也会正常释放锁。

题外话:

线程中断,只是在线程上打一个中断标志,并不会对运行中的线程有什么影响,具体需要根据这个中断标志干些什么,用户自己去决定。

比如,如果用户在调用lock()获取锁后,发现线程中断了,就直接返回了,而导致没有释放锁,这也是允许的,但是会导致这个锁一直得不到释放,就出现了死锁。

lock.lock(); if (Thread.currentThread().interrupted()) { return ; } lock.unlock();

当然,这里只是举个例子,实际使用肯定是要把lock.lock()后面的代码都放在try...finally...里面的以保证锁始终会释放,这里主要是为了说明线程中断只是一个标志,至于要做什么完全由用户自己决定。

tryLock()方法

尝试获取一次锁,成功了就返回true,没成功就返回false,不会继续尝试。

// ReentrantLock.tryLock() public boolean tryLock() { // 直接调用Sync的nonfairTryAcquire()方法 return sync.nonfairTryAcquire(1); } // ReentrantLock.Sync.nonfairTryAcquire() final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }

tryLock()方法比较简单,直接以非公平的模式去尝试获取一次锁,获取到了或者锁本来就是当前线程占有着就返回true,否则返回false。

tryLock(long time, TimeUnit unit)方法

尝试获取锁,并等待一段时间,如果在这段时间内都没有获取到锁,就返回false。

// ReentrantLock.tryLock() public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { // 调用AQS中的方法 return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } // AbstractQueuedSynchronizer.tryAcquireNanos() public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { // 如果线程中断了,抛出异常 if (Thread.interrupted()) throw new InterruptedException(); // 先尝试获取一次锁 return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout); } // AbstractQueuedSynchronizer.doAcquireNanos() private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { // 如果时间已经到期了,直接返回false if (nanosTimeout <= 0L) return false; // 到期时间 final long deadline = System.nanoTime() + nanosTimeout; final Node node = addWaiter(Node.EXCLUSIVE); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return true; } nanosTimeout = deadline - System.nanoTime(); // 如果到期了,就直接返回false if (nanosTimeout <= 0L) return false; // spinForTimeoutThreshold = 1000L; // 只有到期时间大于1000纳秒,才阻塞 // 小于等于1000纳秒,直接自旋解决就得了 if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) // 阻塞一段时间 LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }

tryLock(long time, TimeUnit unit)方法在阻塞的时候加上阻塞时间,并且会随时检查是否到期,只要到期了没获取到锁就返回false。

unlock()方法

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wspzpp.html