目录
parkNanos(Object var0, long nanos)
parkUntil(Object var0, long deadline)
上节末尾我们提到了当需要阻塞或者唤醒一个线程的时候,都会用LockSupport工具类来完成相应的工作,那它具体的原理是什么样的呢?和Condition接口又有什么不同?本节我们就来扒一扒!
LockSupport
LockSupport内部定义了一组以park开头的方法用来阻塞当前线程,以及unpark方法来唤醒一个被阻塞的线程。
方法名称 | 描述 |
void park() | 阻塞当前线程。如果用unpark()方法或者当前线程被中断,才能从park()方法返回。 |
void parkNanos(long nanos) | 阻塞当前线程,最长不超过nanos秒,在park()方法基础上加了超时返回。 |
void parkUntil(long deadlint) | 阻塞当前线程,直到deadlint时间(毫秒数) |
void unpark(Thread thread) | 唤醒处于阻塞状态的线程thread |
在java 6 中新增了void park(Object blocker)、parkNanos(Object blocker,long nanos)、parkUntil(Object blocker,long deadlint)方法,其中blocker用来阻塞对象,即线程被阻塞时谁被阻塞的,主要用于问题排查和系统监控。
看一下park()的源码:
阻塞线程
park(Object var0)
public static void park(Object var0) {
Thread var1 = Thread.currentThread();
setBlocker(var1, var0);
UNSAFE.park(false, 0L);
setBlocker(var1, (Object)null);
}
- 记录当前线程的等待对象。
- 阻塞当前线程。
- 将当前线程对象设置为null。
setBlocker(t, blocker)方法的作用是记录t线程是被broker阻塞的,所以核心方法便是UNSAFE.park,UNSAFE是一个Native类,是基于底层的,可以直接操作内存。
private static void setBlocker(Thread var0, Object var1) {
UNSAFE.putObject(var0, parkBlockerOffset, var1);
}
对给定线程t的parkBlocker赋值。
public static Object getBlocker(Thread var0) {
if (var0 == null) {
throw new NullPointerException();
} else {
return UNSAFE.getObjectVolatile(var0, parkBlockerOffset);
}
}
从给定的线程中获取parkBlocker对象,即返回的是阻塞线程的Blocker对象。
parkNanos(Object var0, long nanos)
public static void parkNanos(Object var0, long nanos) {
if (var1 > 0L) {
Thread var3 = Thread.currentThread();
setBlocker(var3, var0);
UNSAFE.park(false, nanos);
setBlocker(var3, (Object)null);
}
}
阻塞当前线程,最长不超过nanos纳秒。
parkUntil(Object var0, long deadline)
public static void parkUntil(Object var0, long deadline) {
Thread var3 = Thread.currentThread();
setBlocker(var3, var0);
UNSAFE.park(true, deadline);
setBlocker(var3, (Object)null);
}
阻塞当前线程直到deadline时间。
唤醒线程
park(Object var0)
public static void unpark(Thread var0) {
if (var0 != null) {
UNSAFE.unpark(var0);
}
}
唤醒处于阻塞状态的线程var0。
Condition接口
任意一个java对象(java.long.Object),都有一租监视器,里面有很多方法:
红框中的这些方法,与synchronized同步关键字(state)配合,可以实现等待/通知模式 。Condition接口也实现了类似Object的监视器方法,于Lock配合可以实现等待/通知模式。
- 以
await
开头的方法使当前线程进入等待队列; - 以
signal
开头的方法使当前线程从等待队列进入到AQS的同步队列中;
Condition对象是由Lock对象创建的,condition需要由Lock对象调用。
使用方式如下:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void conditionWait() throws InterruptedException {
lock.lock();
try {
condition.await();
}finally {
lock.unlock();
}
}
public void conditionSignal(){
lock.lock();
try{
condition.signal();
}finally {
lock.unlock();
}
}
void await():当前线程进入等待状态直到被通知(signal)或中断。
void awaitUntinterruotibly():当前线程进入等待状态直到被通知,对中断不敏感。
long awaitNanos(long nanos):当前线程进入等待状态直到被通知、中断、或者超时。返回值表示剩余的时间,如果返回值是0或者负数,认定已经超时。
boolean awaitUntil(Data dealine):当前线程进入等待状态直到被通知、中断、或者到某个时间。如果没有到指定时间就被通知,返回true,否则返回false。
Condition的ait()方法:
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);
}
调用该方法的线程必定是获取了锁的,所以不需要通过CAS来保证线程安全,调用该方法是,该方法会将当前线程构造成节点并加入等待队列中,然后释放锁,并且唤醒同步队列中的后继节点,然后当前线程进入等待状态。
同步队列的首节点不是直接加入到等待队列,而是通过addConditionWaiter方法把当前线程构造成一个新的节点并加入等待队列。
Condition的signal()方法
public final void signal() {
//判断是否获取了锁
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
调用该方法的前提是当前线程必须获取了锁,然后将其移动到同步队列并使用LockSupport唤醒节点中的线程 。
private void doSignal(Node first) {
do {
// 当前首节点出队列
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
// 将condition的queue中节点移到同步队列中
// Transfers a node from a condition queue onto sync queue.
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 将当前node加入到同步队列中,等待获取锁
Node p = enq(node);
int ws = p.waitStatus;
//告诉前驱节点,在释放状态是通知自己。
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
被唤醒的线程将从await()方法中的whilex循环中退出,进而调用同步器的acquireQueued()方法加入到获取锁的竞争中。
Condition的signalAll()方法,相当于对等待队列中的每一个节点均执行一次signal()方法,效果就是将等待队列中所有节点全部移动到同步队列中,并唤醒每个节点的线程。
LockSupport 与Condition的不同:
- LockSupport不需要获取对象的锁。
- 实现机制不同,lockSupport 调用 native的park ,unpark方法。
参考:《Java并发编程艺术》 --方腾飞