Java 线程同步基础类 LockSupport 解析
来源:互联网 发布:小夕kitty淘宝 编辑:程序博客网 时间:2024/06/06 20:43
- 概述
- 源码解析
- 灵活的特性
- 应用
- AQS AbstractQueuedSynchronizer
- FutureTask
- 参考资料
概述
LockSupport 类提供了基本的线程同步原语,是实现 AbstractQueuedSynchronizer 和 ReentrantLock 的基础。
源码解析
LockSupport 最重要的就是加锁的 park、解锁的 unpark 方法了:
public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(false, 0L); setBlocker(t, null); } private static void setBlocker(Thread t, Object arg) { UNSAFE.putObject(t, parkBlockerOffset, arg); } public static void unpark(Thread thread) { if (thread != null) unsafe.unpark(thread); }
park 的入参 blocker 是阻塞线程的对象,通过 setBlocker 方法设置到 Thread 对象的 parkBlocker 域,用于线程监控和分析。unpark 的入参是将要解锁的线程。parker 方法的注释里提到了三种情况会造成 park 返回:unpark、中断、spurious wakeup。
Some other thread invokes {@link #unpark unpark} with the current thread as the target.
Some other thread {@linkplain Thread#interrupt interrupts} the current thread.
The call spuriously (that is, for no reason) returns.
关于 spurious wakeup 可以看看参考文献里的资料。
park 和 unpark 方法实际上是调用了 Unsafe 类里的函数:
// in sun.misc.Unsafe.java // thread 参数标记要唤醒的线程 public native void unpark(Object thread); // time 为 0L 时,表示不设置超时时间 // isAbsolute 参数为 true,表示 time 为超时唤醒的绝对时间;否则为相对时间 public native void park(boolean isAbsolute, long time);
从上面的代码可知,park 时可以设置超时时间,对应的 LockSupport 中也有能够设置绝对超时时间的 parkUntil,和相对超时时间的 parkNanos:
public static void parkUntil(long deadline) { UNSAFE.park(true, deadline); } public static void parkNanos(long nanos) { if (nanos > 0) UNSAFE.park(false, nanos); }
Unsafe 的 park 和 unpark 又是怎么实现的呢?在底层,每一个 Thread 对象的都有一个 Parker 实例,
// in vm/runtime/thread.hpp // JSR166 per-thread parkerprivate: Parker* _parker;public: Parker* parker() { return _parker; }
Parker 类继承自 PlatformParker,PlatformParker 的实现就是系统相关了,linux 的实现是利用了 POSIX 的 mutex 和 condition。
class 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();};// in linux/vm/os_linux.hppclass PlatformParker : public CHeapObj<mtInternal> { protected: pthread_mutex_t _mutex [1] ; pthread_cond_t _cond [2] ; // one for relative times and one for abs.};
Parker::park 方法中,主要是调用了 pthread_cond_wait 方法和 safe_cond_timedwait,safe_cond_timedwait 调用了 pthread_cond_timedwait。
void Parker::park(bool isAbsolute, jlong time) { // 这中间省略了很多代码 if (time == 0) { _cur_index = REL_INDEX; // arbitrary choice when not timed status = pthread_cond_wait (&_cond[_cur_index], _mutex) ; } else { _cur_index = isAbsolute ? ABS_INDEX : REL_INDEX; status = os::Linux::safe_cond_timedwait (&_cond[_cur_index], _mutex, &absTime) ; if (status != 0 && WorkAroundNPTLTimedWaitHang) { pthread_cond_destroy (&_cond[_cur_index]) ; pthread_cond_init (&_cond[_cur_index], isAbsolute ? NULL : os::Linux::condAttr()); } } // 这里也省略了很多代码 status = pthread_mutex_unlock(_mutex) ; assert_status(status == 0, status, "invariant") ; // 内存栅栏,内存屏障 OrderAccess::fence();}int os::Linux::safe_cond_timedwait(pthread_cond_t *_cond, pthread_mutex_t *_mutex, const struct timespec *_abstime){ if (is_NPTL()) { return pthread_cond_timedwait(_cond, _mutex, _abstime); } else { // 6292965: LinuxThreads pthread_cond_timedwait() resets FPU control // word back to default 64bit precision if condvar is signaled. Java // wants 53bit precision. Save and restore current value. int fpu = get_fpu_control_word(); int status = pthread_cond_timedwait(_cond, _mutex, _abstime); set_fpu_control_word(fpu); return status; }}
Parker::unpark 设置 _counter 为1,再 unlock mutex,如果 _counter 之前小于 1,则调用 pthread_cond_signal 唤醒等待的线程。
void Parker::unpark() { int s, status ; status = pthread_mutex_lock(_mutex); assert (status == 0, "invariant") ; s = _counter; _counter = 1; if (s < 1) { // thread might be parked if (_cur_index != -1) { // thread is definitely parked if (WorkAroundNPTLTimedWaitHang) { status = pthread_cond_signal (&_cond[_cur_index]); 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[_cur_index]); assert (status == 0, "invariant"); } } else { pthread_mutex_unlock(_mutex); assert (status == 0, "invariant") ; } } else { pthread_mutex_unlock(_mutex); assert (status == 0, "invariant") ; }}
灵活的特性
在 Parker::park 中有这么一段代码,如果 _counter 大于 0,则立即返回。在上面 unpark 的代码中,我们看到 unpark 将 _counter 设置为 1,也就是说:两个线程之间的 park 和 unpark 不存在时序关系,可以先 unpark 再 park,不会造成死锁。这相对于存在依赖关系的 wait/notify 机制是一个巨大的优点。
void Parker::park(bool isAbsolute, jlong time) { // 这中间省略了很多代码 int status ; if (_counter > 0) { // no wait needed _counter = 0; status = pthread_mutex_unlock(_mutex); assert (status == 0, "invariant") ; // Paranoia to ensure our locked and lock-free paths interact // correctly with each other and Java-level accesses. OrderAccess::fence(); return; } // 这后面省略了更多代码
应用
AQS: AbstractQueuedSynchronizer
AbstractQueuedSynchronizer 中获取锁的代码如下,这里使用 for 循环,而不是在 park 返回后就立即返回,也是为了排除中断、虚假唤醒等并非因资源可用而唤醒的情况。
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); } }
FutureTask
在 FutureTask 中,等待操作完成的 awaitDone 大致分为以下步骤:
1. 先检查是否存在中断,是则抛异常 InterruptedException;
2. 是否已经完成,是则返回;
3. 进入等待队列中;
4. 当设置了超时时间 nanos 时,调用 LockSupport.parkNanos 方法等待;
5. 没有设置超时时间时,调用 LockSupport.park 方法等待。
private int awaitDone(boolean timed, long nanos) throws InterruptedException { final long deadline = timed ? System.nanoTime() + nanos : 0L; WaitNode q = null; boolean queued = false; for (;;) { if (Thread.interrupted()) { removeWaiter(q); throw new InterruptedException(); } int s = state; if (s > COMPLETING) { if (q != null) q.thread = null; return s; } else if (s == COMPLETING) // cannot time out yet Thread.yield(); else if (q == null) q = new WaitNode(); else if (!queued) queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q); else if (timed) { nanos = deadline - System.nanoTime(); if (nanos <= 0L) { removeWaiter(q); return state; } LockSupport.parkNanos(this, nanos); } else LockSupport.park(this); } }
参考资料
Java 的 LockSupport.park() 实现分析
多线程编程中条件变量和虚假唤醒(spurious wakeup)的讨论
Why does pthread_cond_wait have spurious wakeups?
- Java 线程同步基础类 LockSupport 解析
- java基础线程-LockSupport
- 【java基础】线程笔记——LockSupport
- java基础:线程同步
- java基础--线程同步
- Java线程阻塞中断和LockSupport问题
- Java线程阻塞中断和LockSupport问题
- Java并发工具类LockSupport
- Java基础--多线程|线程同步
- Java基础-线程同步-锁
- java LockSupport
- java LockSupport
- Java LockSupport
- 【java基础之多线程】线程同步
- LockSupport类
- java线程阻塞中断与LockSupport使用介绍
- Java线程阻塞中断和LockSupport的常见问题
- java线程阻塞中断和LockSupport的常见问题
- jQuery callback源码分析
- 高斯消元(Gauss elimination)的算法实现
- 调试——Debug
- java基础数组案例
- 9.12第一轮扫荡战果
- Java 线程同步基础类 LockSupport 解析
- Java实现几种常见排序方法
- 【CUGBACM15级BC第35场 A】hdu 5194 DZY Loves Balls
- JAVA入门学习小结(二)枚举类型
- JavaSE socket 基于UDP Server/Client的实现
- 20170912_字符串拷贝strcpy的实现
- 纯虚函数和抽象类
- Python的global关键字及全局变量的用法
- ThreadLocal实现原理