java源码分析:重入锁ReentrantLock
来源:互联网 发布:caffe 前向计算 编辑:程序博客网 时间:2024/06/05 23:05
可重入:一个线程可以连续多次获得锁
加锁
Lock.lock() 获取锁
final void lock() { acquire(1); }
很简单,就是调用acqire函数,后面的参数1表示,如果获得了锁,对于另外进程释放而获取获取的,那么就锁的值就是1,否则如果是重入的值+1
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
首先通过tryAcquire获取锁,如果没有获取,那么首先通过addWaiter把当前线程放到等待队列的最后面,并阻塞它。
而在tryAcquire中,根据是否公平锁有两个版本,先看公平锁
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; }
这个函数就是尝试去获取锁,返回是否获取了锁。首先获取锁的状态,看是否为0,如果是0,那么当前是没有线程占据锁的,那么判断当前线程是否在队列的第一个,如果是那么通过原子的compareAndSetState获取锁,把当前线程设置为锁的获得者,返回true。如果当前锁是被线程占领的,那么看下是不是被当前线程占了,如果是的话,把对应的持有锁的次数+acquires。 如果没有成功获取锁,那么就会调用addWaiter和acquireQueued两个函数,他们两个分别是把线程放到了锁的队列最后面,然后把线程给阻塞了。
private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; }
以当前的线程为信息,新建一个节点放到列表的最后面。
然后来看下acquireQueued
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)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
这块代码不断循环做两件事,第一件判断当前线程是不是队列的最前面,如果是的话,那么再次尝试获取锁,成功就把当前节点设置为头并返回是否被中断过。 否则就干第二件事,调用shouldParkAfterFailedAcquire决定是否把当前进行给阻塞,如果需要那么通过parkAndCheckInterrupt把线程阻塞,阻塞成功把interrupted设置true。
我们在来看下shouldParkAfterFailedAcquire这个函数
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; if (ws == Node.SIGNAL) /* * This node has already set status asking a release * to signal it, so it can safely park. */ return true; if (ws > 0) { /* * Predecessor was cancelled. Skip over predecessors and * indicate retry. */ do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { /* * waitStatus must be 0 or PROPAGATE. Indicate that we * need a signal, but don't park yet. Caller will need to * retry to make sure it cannot acquire before parking. */ compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }
首先看当前线程的前个节点的状态,如果是SIGNAL,那么他们都没有获取锁,那就直接返回true表示进入阻塞状态
其次:把前面节点中是cancelled状态的给删除了,把当前节点作为第一个非cancelled进程的下游
否则就设置前继的状态为SIGNAL,这样下次循环可以进入1而进入阻塞
如果需要阻塞,通过下面函数完成
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
阻塞,如果阻塞结束后,返回线程的中断状态。
# 参考
http://blog.csdn.net/yanyan19880509/article/details/52345422
- java源码分析:重入锁ReentrantLock
- java ReentrantLock源码分析
- Java并发-ReentrantLock源码分析
- java并发-ReentrantLock源码分析
- java并发-ReentrantLock源码分析
- Java 并发 ---ReentrantLock源码分析
- java ReentrantLock源码 分析 妥妥的
- 《Java源码分析》:ReentrantLock.lock 锁机制
- 《Java源码分析》:ReentrantLock.unlock 释放锁
- 《Java源码分析》:ReentrantLock.lock 锁机制
- 《Java源码分析》:ReentrantLock.unlock 释放锁
- ReentrantLock 源码分析
- ReentrantLock源码分析
- JUC - ReentrantLock 源码分析
- ReentrantLock 源码分析
- ReentrantLock 源码分析
- ReentrantLock源码分析(一)
- ReentrantLock源码分析(二)
- 剑指offer 丑数
- Iaas Paas Saas三种云服务区别
- Android学习之Java基础篇day3
- CakePHP系列(二)----博客(Blog)案例(二)
- 数据结构之线性表
- java源码分析:重入锁ReentrantLock
- MFC中将图片显示到picture控件上
- 赵家新村水阁台面将维缮
- 有关 php __autoload 自动加载类函数的用法
- 操作系统常见面试题整理(一)
- C++知识点7
- 看商品猜价格
- 数据库面试题1
- [操作系统概念]第六部分——内存管理(2):分页分段