java 语言中谈到锁,少不了比较一番 synchronized 和 ReentrantLock 的原理,本文不作分析,只是简单介绍一下 ReentrantLock 的用法,从使用中推测其内部的一些原理。

代码示例:

public static void main(String[] args) throws InterruptedException {
final ReentrantLock lock = new ReentrantLock();
final Condition con1 = lock.newCondition();
final Condition con2 = lock.newCondition(); // lock.lock(); Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
con1.await();
System.out.println("wake from cond1");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
t1.start(); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
con2.await();
System.out.println("wake from cond2");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
t2.start(); Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
con2.await();
System.out.println("wake from cond2");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
t3.start(); Thread.sleep(100);
try {
lock.lock();
System.out.println("lock.getQueueLength() = " + lock.getQueueLength());
System.out.println("lock.getWaitQueueLength(con1) = " + lock.getWaitQueueLength(con1));
System.out.println("lock.getWaitQueueLength(con2) = " + lock.getWaitQueueLength(con2));
con1.signal();
con2.signalAll(); Thread.sleep(100);
System.out.println("lock.getWaitQueueLength(con1) = " + lock.getWaitQueueLength(con1));
System.out.println("lock.getWaitQueueLength(con2) = " + lock.getWaitQueueLength(con2));
} finally {
lock.unlock();
} }

lock 和 await 会改变 state 的值。

以 ReentrantLock.getQueueLength 和 ReentrantLock.getWaitQueueLength 作为入口:前者作用是返回获取锁失败,处于等待状态的线程个数,后者是返回等待某一个 condition 的线程个数。

// int java.util.concurrent.locks.ReentrantLock.getQueueLength()
public final int getQueueLength() {
return sync.getQueueLength();
} // int java.util.concurrent.locks.AbstractQueuedSynchronizer.getQueueLength()
public final int getQueueLength() {
int n = 0;
for (Node p = tail; p != null; p = p.prev) {
if (p.thread != null)
++n;
}
return n;
}

很简单,就是遍历阻塞线程的链表。

// int java.util.concurrent.locks.ReentrantLock.getWaitQueueLength(Condition condition)
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
} // int java.util.concurrent.locks.AbstractQueuedSynchronizer.getWaitQueueLength(ConditionObject condition)
public final int getWaitQueueLength(ConditionObject condition) {
if (!owns(condition))
throw new IllegalArgumentException("Not owner");
return condition.getWaitQueueLength();
} // int java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject.getWaitQueueLength()
protected final int getWaitQueueLength() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int n = 0;
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
if (w.waitStatus == Node.CONDITION)
++n;
}
return n;
}

同样,也是遍历链表,不同的是,这是 Condition 的链表。

现在,我们知道,ReentrantLock 中有 2 种不同的链表,其一是阻塞线程,其二是 Condition 等待链表,2 种链表都是使用 Node:

// java.util.concurrent.locks.AbstractQueuedSynchronizer.Node
static final class Node {
static final Node EXCLUSIVE = null;
// 线程阻塞节点的状态
static final int CANCELLED = 1;
static final int SIGNAL = -1;
// condition 节点的状态
static final int CONDITION = -2;
static final int PROPAGATE = -3;
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
}

节点的类型表明链表的类型。

看看 Condition 实现类的 api:

public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
private transient Node firstWaiter;
private transient Node lastWaiter; /**
* Adds a new waiter to wait queue.
* @return its new wait node
*/
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
} public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
} /**
* Moves the longest-waiting thread, if one exists, from the
* wait queue for this condition to the wait queue for the
* owning lock.
*
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
* returns {@code false}
*/
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
}

在开头的示例中,创建了 2 个 Condition 对象,每个Condition 对象有一个自己的等待链表。

最新文章

  1. "SQL Server does not handle comparison of NText, Text, Xml, or Image data types."
  2. protobuf学习(1)-ubuntu14.04下protobuf2.6安装
  3. Poisson泊松分布
  4. LR连接oracle时出现:SQLState=28000[Oracle][ODBC][Ora]ORA-01017:invalid username/password;logon denied
  5. 【BZOJ】1038: [ZJOI2008]瞭望塔
  6. [网络技术][转]PPTP连接过程
  7. HDU-1700 Points on Cycle
  8. QUARTZ CRON
  9. Python中的变量
  10. 已知有两个水杯,一个11L一个7L,水可以任意使用,求怎么得到2L 的详细解法
  11. 51NOD 1227 平均最小公倍数 [杜教筛]
  12. LeetCode前100题(EASY难度)
  13. JSP中request获取值
  14. iis url rewrite http->https non-www->www
  15. JavaScript的使用你知道几种?(上)
  16. php利用array_search与array_column实现二维数组查找
  17. Netty handler处理类无法使用@Autowired注入bean的解决方法
  18. Model中时间格式化
  19. c# winform 获取当前程序运行根目录
  20. 第二阶段站立会议alpha版总结

热门文章

  1. 项目Alpha冲刺--5/10
  2. React入门实例:组件化+react-redux实现网上商城(1)
  3. _killerstreak
  4. zipkin启动报错(Caused by: java.lang.ClassNotFoundException: zipkin.Component)的解决方法
  5. 1. eclipse异常问题解决办法
  6. gensim使用方法以及例子
  7. Boostrap导航栏跳转到其他页面或外部链接
  8. Qt基本布局(QLayout)
  9. RPG游戏中如何判断敌人是否在玩家的攻击范围之内
  10. GrindEQ Math Utilities 2015破解版 图文安装和序列号补丁激活教程