从源码看Lock特性(1)之 ReentrantLock
多线程操作时,当出现资源竞争的场景,就需要考虑线程死锁的场景,java提供了两种方案:使用关键字 synchronized 和 Lock 类。今天来说下 jdk concurrent包的两种锁,一种是可重入锁,一种是可重入读写锁(后面再说)。
- 可重入锁
可重入锁的可重入是什么意思?我是这么理解的,以synchronized为例,A方法调用了B方法,而且A、B方法都用 synchronized 修饰,那在调用B方法时,对象锁已经被拿到了,在调用B方法时,拿不到对象锁,那么调用B方法的线程是不是需要等待,这时,A方法内部的语句还没有执行完(也就是B方法没有执行完,在等待对象锁,处于阻塞状态),是不是就出现死锁了,但是实际上并没有出现这种情况,执行方法A的线程拿到对象锁后,在执行B方法时,判断拿到对象锁的线程是自己,所以继续执行B方法的逻辑。代码如下:
public void A(){ synchronized(this){ System.out.println("a method"); B(); } } public void B(){ synchronized (this) { System.out.println("b method"); } }
ReentrantLock是jdk可重入锁的实现,它有两个内部类 FairSync 和 NonFairSync 类都继承 Sync,顾名思义一个是公平锁,一个是不公平锁(也就是抢占锁)。而Sync继承于AbstractQueuedSynchronizer;AbstractQueuedSynchronizer 内部有一个 Node 类,而 Node 分为两种模式一种是共享模式,另一种是独有模式,这两种模式对应了两种锁——共享锁和排它锁。AbtsractQueuedSynchronizer 持有 head 和 tail (双向链表数据结构)的 Node,当你调用 lock 方法的时候,首先尝试获取锁,没有其它的线程获取锁了,或者锁被当前线程持有,然后会在链的末端插入一个 独占模式的Node,然后去请求锁;请求锁时,如果有线程在阻塞等待锁时,当获得锁的线程时当前线程,则本次请求锁会滑到链表的节点头判断是否时同一个线程,再给锁,否则,线程将被阻塞。主要的代码如下
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable { /** * Head of the wait queue, lazily initialized. Except for * initialization, it is modified only via method setHead. Note: * If head exists, its waitStatus is guaranteed not to be * CANCELLED. */ private transient volatile Node head; /** * Tail of the wait queue, lazily initialized. Modified only via * method enq to add new wait node. */ private transient volatile Node tail; /** * The synchronization state. */ private volatile int state;
}
static final class Node { /** Marker to indicate a node is waiting in shared mode */ static final Node SHARED = new Node(); /** Marker to indicate a node is waiting in exclusive mode */ static final Node EXCLUSIVE = null; }
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ 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 void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
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; }
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); } }

浙公网安备 33010602011771号