lock/unlockLockSupport 是用来实现堵塞语义模型的基础辅助类 , 主要有两个方法:park 与 unpark 。(在英文中 , park 除了“公园”含义外 , 还有“停车”的意思)
// LockSupport.javapublic static void unpark(Thread thread) {if (thread != null)UNSAFE.unpark(thread);}public static void park(Object blocker) {Thread t = Thread.currentThread();setBlocker(t, blocker);UNSAFE.park(false, 0L);setBlocker(t, null);}// Unsafe.java/*** Unblocks the given thread blocked on {@code park}, or, if it is* not blocked, causes the subsequent call to {@code park} not to* block.Note: this operation is "unsafe" solely because the* caller must somehow ensure that the thread has not been* destroyed. Nothing special is usually required to ensure this* when called from Java (in which there will ordinarily be a live* reference to the thread) but this is not nearly-automatically* so when calling from native code.** @param thread the thread to unpark.*/@HotSpotIntrinsicCandidatepublic native void unpark(Object thread);/*** Blocks current thread, returning when a balancing* {@code unpark} occurs, or a balancing {@code unpark} has* already occurred, or the thread is interrupted, or, if not* absolute and time is not zero, the given time nanoseconds have* elapsed, or if absolute, the given deadline in milliseconds* since Epoch has passed, or spuriously (i.e., returning for no* "reason"). Note: This operation is in the Unsafe class only* because {@code unpark} is, so it would be strange to place it* elsewhere.*/@HotSpotIntrinsicCandidatepublic native void park(boolean isAbsolute, long time);// hotspot/share/prims/unsafe.cppUNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time)) {HOTSPOT_THREAD_PARK_BEGIN((uintptr_t) thread->parker(), (int) isAbsolute, time);EventThreadPark event;JavaThreadParkedState jtps(thread, time != 0);thread->parker()->park(isAbsolute != 0, time);if (event.should_commit()) {post_thread_park_event(&event, thread->current_park_blocker(), time);}HOTSPOT_THREAD_PARK_END((uintptr_t) thread->parker());} UNSAFE_END通过上述 unsafe.cpp 可以看到每个 thread 都会有一个 Parker 对象 , 所以我们需要查看 parker 对象的定义
// hotspot/share/runtime/park.hppclass Parker : public os::PlatformParker...public:// For simplicity of interface with Java, all forms of park (indefinite,// relative, and absolute) are multiplexed into one call.void park(bool isAbsolute, jlong time);void unpark();// hotspot/os/posix/os_posix.hppclass PlatformParker : public CHeapObj<mtInternal> { protected:enum {REL_INDEX = 0,ABS_INDEX = 1};int _cur_index;// which cond is in use: -1, 0, 1pthread_mutex_t _mutex[1];pthread_cond_t_cond[2]; // one for relative times and one for absolute...};看到这里大概就能知道 park 是使用 pthread_mutex_t 与 pthread_cond_t 实现 。好了 , 到目前为止 , 就引出了 Java 中与堵塞相关的实现 , 不难想象 , 都是依赖底层操作系统的功能 。
OS 支持的同步原语Semaphore并发编程领域的先锋人物 Edsger Dijkstra(也是最短路径算法的作者)在 1965 年首次提出了信号量( Semaphores) 这一概念来解决线程同步的问题 。信号量是一种特殊的变量类型 , 为非负整数 , 只有两个特殊操作PV:
- P(s) 如果 s!=0 , 将 s-1;否则将当前线程挂起 , 直到 s 变为非零
- V(s) 将 s+1 , 如果有线程堵塞在 P 操作等待 s 变成非零 , 那么 V 操作会重启这些线程中的任意一个
struct semaphore {int val;thread_list waiting;// List of threads waiting for semaphore}wait(semaphore Sem):// Wait until > 0 then decrement// 这里用的是 while 而不是 if// 这是因为在 wait 过程中 , 其他线程还可能继续调用 waitwhile (Sem.val <= 0) {add this thread to Sem.waiting;block(this thread);}Sem.val = Sem.val - 1;return;signal(semaphore Sem):// Increment value and wake up next threadSem.val = Sem.val + 1;if (Sem.waiting is nonempty) {remove a thread T from Sem.waiting;wakeup(T);}有两点注意事项:- wait 中的「测试和减 1 操作」 , signal 中的「加 1 操作」需要保证原子性 。一般来说是使用硬件支持的 read-modify-write 原语 , 比如 test-and-set/fetch-and-add/compare-and-swap , 除了硬件支持外 , 还可以用 busy wait 的软件方式来模拟 。
- signal 中没有定义重新启动的线程顺序 , 也即多个线程在等待同一信号量时 , 无法预测重启哪一个线程
推荐阅读
- Java5,6,7,8的主要新特性归纳
- Java环境搭建,环境变量配置
- IPv6基础了解和配置
- JavaScript 字符串中的 pad 方法
- Java中的21种锁,图文并茂的详细解释
- Javascript中类型知识和valueOf和toString()方法
- javascript事件流
- 盘点2020JavaScript游戏框架
- Java分布式锁看这篇就够了
- 网络高并发
