Java并发(九):重入锁 ReentrantLock
2018-11-12 06:51:42来源:博客园 阅读 ()
一、ReentrantLock类结构
public class ReentrantLock implements Lock, java.io.Serializable { private final Sync sync; // 锁 大部分功能都是委托给Sync来实现的 abstract static class Sync extends AbstractQueuedSynchronizer {} static final class FairSync extends Sync {} static final class NonfairSync extends Sync {} }
二、以NonfairSync为例解析重入锁
获取锁标志:
(NonfairSync extends Sync extends AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer)
1.AbstractQueuedSynchronizer.state>0(0代表没有被占用,大于0代表有线程持有当前锁(锁可以重入,每次重入都+1))
2.AbstractOwnableSynchronizer.exclusiveOwnerThread == Thread.currentThread()
获取锁:
public static void main(String[] args) { ReentrantLock lock = new ReentrantLock();// 默认是非公平锁 lock.lock(); } // ReentrantLock public void lock() { sync.lock(); } // NonfairSync final void lock() { if (compareAndSetState(0, 1)) // 尝试获取锁 setExclusiveOwnerThread(Thread.currentThread()); // 如果拿到锁就设置当前线程 else acquire(1); } // AbstractQueuedSynchronizer public final void acquire(int arg) { if (!tryAcquire(arg) && // 尝试获取锁 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 没有获取到锁,将线程加入同步队列(参考上一篇AbstractQueuedSynchronizer) selfInterrupt(); } // NonfairSync protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } /** * 获取锁标志: * (NonfairSync extends Sync extends AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer) * 1.AbstractQueuedSynchronizer.state>0(0代表没有被占用,大于0代表有线程持有当前锁(锁可以重入,每次重入都+1)) * 2.AbstractOwnableSynchronizer.exclusiveOwnerThread == Thread.currentThread() * Sync(NonfairSync没有重写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; }
释放锁:
// ReentrantLock public void unlock() { sync.release(1); } // AbstractQueuedSynchronizer public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); // 唤醒队列下一个节点线程 参考上一篇:AbstractQueuedSynchronizer return true; } return false; } // Sync protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; // 重入锁,直到state==0才算释放 if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }
三、公平锁与非公平锁
// FairSync(NonfairSync会先尝试拿锁,FairSync不会) final void lock() { acquire(1); } // FairSync protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && // CLH队列为空或者队列头结点是当前线程节点 才能获得锁 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; } /** * AbstractQueuedSynchronizer * true - CLH队列为空或者队列头结点是当前线程节点 */ public final boolean hasQueuedPredecessors() { Node t = tail; Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
以上代码可以看出,公平锁和非公平锁只有两处不同:
(1)非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁。
(2)非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法。在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,而公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。
公平锁和非公平锁就这两点区别,如果这两次 CAS 都不成功,那么后面非公平锁和公平锁是一样的,都要进入到阻塞队列等待唤醒。
因此,非公平锁会有更好的性能,因为它的吞吐量比较大。当然,非公平锁让获取锁的时间变得更加不确定,可能会导致在阻塞队列中的线程长期处于饥饿状态。
四、ReentrantLock优势
ReentrantLock与synchronized具有相同的功能和内存语义。
1、与synchronized相比,ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间锁等候,可中断锁等候,锁投票。
2、ReentrantLock还提供了条件Condition,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock更加适合。
3、ReentrantLock提供了可轮询的锁请求。它会尝试着去获取锁,如果成功则继续,否则可以等到下次运行时处理,而synchronized则一旦进入锁请求要么成功要么阻塞,所以相比synchronized而言,ReentrantLock会不容易产生死锁些。
4、ReentrantLock支持更加灵活的同步代码块,但是使用synchronized时,只能在同一个synchronized块结构中获取和释放。注:ReentrantLock的锁释放一定要在finally中处理,否则可能会产生严重的后果。
5、ReentrantLock支持中断处理,且性能较synchronized会好些。
参考资料 / 相关推荐
【死磕Java并发】—–J.U.C之重入锁:ReentrantLock
一行一行源码分析清楚AbstractQueuedSynchronizer
Java并发(八):AbstractQueuedSynchronizer
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- 国外程序员整理的Java资源大全(全部是干货) 2020-06-12
- 2020年深圳中国平安各部门Java中级面试真题合集(附答案) 2020-06-11
- 2020年java就业前景 2020-06-11
- 04.Java基础语法 2020-06-11
- Java--反射(框架设计的灵魂)案例 2020-06-11
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash