java.util.concurrent.locks包中的ReentrantLock之非公平策略解析
Posted charging-for-ycp
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java.util.concurrent.locks包中的ReentrantLock之非公平策略解析相关的知识,希望对你有一定的参考价值。
简介:
ReentrantLock作为一个可重入互斥锁,具有与Synchronized隐式监视器相同的功能,除此之外,还有更强的扩展性。
如果一个线程调用lock(),如果该锁未被另外一个线程持有,则成功获取锁并返回;如果当前线程已经持有该锁,则直接返回。可以通过isHeldByCurrentThread() 和 getHoldCount()查看该线程是否已持有该锁以及次数。
Public ReentrantLock(boolean fairness):此构造器,可以通过设置fairness=true来保证ReentrantLock的公平性策略:等待时间最长的线程优先获得锁。采用公平策略比采用默认策略(非公平策略)的lock,在程序吞吐量方面前者远远低于后者;在尝试获取锁的次数与避免饥饿性方面却有着较小的差异。原文描述:(Programs using fair locks accessed by many threads may display lower overall throughput (i.e., are slower; often much slower) than those using the default setting, but have smaller variances in times to obtain locks and guarantee lack of starvation)。除此之外,锁的公平性无法保证线程调度的公平性。因此,可能存在以下情况:其中一个线程多次成功获取该锁,导致其他线程无法获取该锁。需要注意的是,tryLock()方法,在原线程释放锁的时候,即使等待队列中存在其他线程,那么调用tryLock()的线程依然有可能提前获得该锁。
当我们调用lock()时后,推荐使用tryfinally 来进行锁的释放。
class X private final ReentrantLock lock = new ReentrantLock(); // ... public void m() lock.lock(); // block until condition holds try // ... method body finally lock.unlock()
ReenttrantLock实现了序列化接口:当其反序列化时,是释放锁状态的,忽略其序列化时的锁状态。
默认情况下,ReenttrantLock采用的非公平策略。由其无参构造函数可以看出。
public ReentrantLock() sync = new NonfairSync();
这里主要介绍lock()与unlock()两种方法。
lock()
1.当我们获取锁的时候,它没有被其他线程持有,则持锁的数量=1,并立即返回;
2.如果该锁已被当前线程持有,则在原先持锁的数量+1,并立即返回;
3.如果该锁被其他线程持有,则该线程则进行阻塞,直到成功获取锁,并将持锁的数量=1.
final void lock()
//第一步 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1);
acquire()
以独占锁模式进行获取,忽略中断(延后中断)。如果尝试获取锁失败,则将该线程包装成一个EXCLUSIVE的Node加入一个等待队列中,通过调用tryAcquire()反复的进行阻塞与解除阻塞,直至成功
public final void acquire(int arg) if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt();
非公平Sync的tryAcquire()实现
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;
获取失败,入队:首先初始化等待队列
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;
//head==tail==null,进行初始化队列 enq(node); return node;
初始化队列
private Node enq(final Node node) for (;;) Node t = tail; if (t == null) // Must initialize if (compareAndSetHead(new Node())) tail = head; else node.prev = t; if (compareAndSetTail(t, node)) t.next = node; return t;
其次在等待队列中尝试获取锁。该方法只会在机器出现异常或者与其他控件发生交互出现异常,才会取消获取锁。
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(Node pred, Node node)
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;
阻塞线程
parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() LockSupport.park(this); return Thread.interrupted();
阻塞线程后,当持有锁的线程释放锁的时候,会通知等待队列中队首Head的后继节点的线程,将其唤醒,重新获取锁。
以上是关于java.util.concurrent.locks包中的ReentrantLock之非公平策略解析的主要内容,如果未能解决你的问题,请参考以下文章