concurrent包是基于AQS (AbstractQueuedSynchronizer)框架的,AQS框架借助于两个类:
- Unsafe(提供CAS操作)
- LockSupport(提供park/unpark操作,底层仍然调用是Unsafe类的park/unpark方法)
因此,LockSupport非常重要。先看一下park和unpark方法怎么用的吧。
示例:
public static void main(String[] args) throws Exception { Thread thread = new Thread(() -> { System.out.println("开始睡眠..."); // 开始睡眠 LockSupport.park(); System.out.println("已被唤醒!"); }); thread.start(); Thread.sleep(1000); System.out.println("睡了1秒钟之后开始唤醒..."); // 开始唤醒 LockSupport.unpark(thread); }
输出:
开始睡眠... 睡了1秒钟之后开始唤醒... 已被唤醒!
从示例中,我们大概就能明白park和unpark方法的作用,park方法类似于wait,unpark方法类似于notify。下面我们再看一下复杂一点的示例,来深刻理解park和unpark方法的作用。
二、底层原理
LockSupport类park方法和unpark方法
public static void park() { UNSAFE.park(false, 0L); } // ... public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread); }
Unsafe.park和Unsafe.unpark的底层实现原理
public native void park(boolean isAbsolute, long time); public native void unpark(Object var1);
每个Java线程都有一个Parker实例,Parker类是这样定义的:
class Parker : public os::PlatformParker { private: volatile int _counter ; ... public: void park(bool isAbsolute, jlong time); void unpark(); ... } class PlatformParker : public CHeapObj<mtInternal> { protected: pthread_mutex_t _mutex [1] ; pthread_cond_t _cond [1] ; ... }
可以看到Parker类实际上用Posix的mutex(),condition来实现的。mutex是操作系统函数,Synchronized底层也是调用mutex互斥锁的。
在Parker类里的_counter字段,就是用来记录“许可”的。
park过程
当调用park时,先尝试能否直接拿到“许可”,即_counter>0时,如果成功,则把_counter设置为0,并返回:
void Parker::park(bool isAbsolute, jlong time) { // Ideally we'd do something useful while spinning, such // as calling unpackTime(). // Optional fast-path check: // Return immediately if a permit is available. // We depend on Atomic::xchg() having full barrier semantics // since we are doing a lock-free update to _counter. if (Atomic::xchg(0, &_counter) > 0) return;
如果不成功,则构造一个ThreadBlockInVM,然后检查_counter是不是>0,如果是,则把_counter设置为0,unlock <wiz_tmp_highlight_tag class="cm-searching">mutex并返回:
ThreadBlockInVM tbivm(jt); if (_counter > 0) { // no wait needed _counter = 0; status = pthread_mutex_unlock(_mutex);
否则,再判断等待的时间,然后再调用pthread_cond_wait函数等待,如果等待返回,则把_counter设置为0,unlock <wiz_tmp_highlight_tag class="cm-searching">mutex并返回:
if (time == 0) { status = pthread_cond_wait (_cond, _mutex) ; } _counter = 0 ; status = pthread_mutex_unlock(_mutex) ; assert_status(status == 0, status, "invariant") ; OrderAccess::fence();
unpark过程
当unpark时,则简单多了,直接设置_counter为1,再unlock <wiz_tmp_highlight_tag class="cm-searching">mutex返回。如果_counter之前的值是0,则还要调用pthread_cond_signal唤醒在park中等待的线程:
void Parker::unpark() { int s, status ; status = pthread_mutex_lock(_mutex); assert (status == 0, "invariant") ; s = _counter; _counter = 1; if (s < 1) { if (WorkAroundNPTLTimedWaitHang) { status = pthread_cond_signal (_cond) ; assert (status == 0, "invariant") ; status = pthread_mutex_unlock(_mutex); assert (status == 0, "invariant") ; } else { status = pthread_mutex_unlock(_mutex); assert (status == 0, "invariant") ; status = pthread_cond_signal (_cond) ; assert (status == 0, "invariant") ; } } else { pthread_mutex_unlock(_mutex); assert (status == 0, "invariant") ; } }