Thread.yield、Thread.sleep、Object.wait、 LockSupport.park 对比
sleep、wait、park区别
- Thread.sleep() 不会释放占有的锁,Object.wait() 会释放会占有的锁
- Thread.sleep() 必须传入时间,Object.wait() 可以传可不传,不传会一直阻塞下去。
- Thread.sleep() 会自动唤醒,然后继续执行。
- Object.wait() 不带时间方法,需要 Object.notify() 唤醒
- Object.wait() 带时间,如果没有notify ,到时间会自动唤醒。
- LockSupport.park 不需要捕获中断。
- LockSupport.park() 方法可以被 LockSupport.unpark() 唤醒
- Thread.sleep() 方法声明上抛出了 InterruptedException 异常
- Thread.park() 不带超时,需要另一个线程执行 unpark 唤醒。
- Thread.yield() 让出 CPU,不会释放锁,进入就绪状态。
小结
- yield 相当于进行一次主动调度,当前线程放弃 CPU 使用权,重新进入 CPU 的运行队列,等待下一次调度。
- sleep wait park 都是借助 pthread_cond_timedwait 实现阻塞,wait 还需要结合 ObjectMonitor 使用
Thread.yield
JNI 方法
public static native void yield();
JNI 入口
JVM_ENTRY(void, JVM_Yield(JNIEnv *env, jclass threadClass)) // ... if (os::dont_yield()) return; // ... if (ConvertYieldToSleep) { os::sleep(thread, MinSleepInterval, false); // 使用sleep替代 } else { os::yield(); // 默认调用os的yield实现 } JVM_END
最终会调用sched_yield()
void os::yield() { sched_yield(); }
复制代码这是一个linux的系统调用,下面是相关的内核代码
SYSCALL_DEFINE0(sched_yield) { do_sched_yield(); return 0; } static void do_sched_yield(void) { // ... current->sched_class->yield_task(rq); // ... schedule(); }
这里就比较清晰了,首先调用当前任务(线程)对应调度类的yield_task()函数,然后调用schedule()函数执行一次重新调度,相当于为当前CPU选择下一个要执行的任务。对于普通线程来说,对应的调度队列是cfs_rq,对应的调度类是cfs_sched_class,对应的yield_task()函数是yield_task_fair()
Thread.sleep
是一个 Java 本地方法
public static native void sleep(long millis) throws InterruptedException;
sleep 入口,如果参数是 0 的话会转换成yield。
JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis)) // ... if (millis == 0) { if (ConvertSleepToYield) { // 默认是false os::yield(); } else { ThreadState old_state = thread->osthread()->get_state(); thread->osthread()->set_state(SLEEPING); os::sleep(thread, MinSleepInterval, false); // 小睡一下 thread->osthread()->set_state(old_state); } } else { ThreadState old_state = thread->osthread()->get_state(); thread->osthread()->set_state(SLEEPING); if (os::sleep(thread, millis, true) == OS_INTRPT) { // 处理中断 } thread->osthread()->set_state(old_state); } // ... JVM_END
调用 os::sleep (jvm 函数,不是操作系统的)
int os::sleep(Thread* thread, jlong millis, bool interruptible) { ParkEvent * const slp = thread->_SleepEvent ; if (interruptible) { jlong prevtime = javaTimeNanos(); for (;;) { if (os::is_interrupted(thread, true)) { return OS_INTRPT; } jlong newtime = javaTimeNanos(); if (newtime - prevtime < 0) { // ... } else { millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC; } if(millis <= 0) { return OS_OK; } // ... { // ... slp->park(millis); // 调用的是os::PlatformEvent::park // ... } } } else { // ... } }
最终调用 ParkEvent 的 park 函数,实现如下:
int os::PlatformEvent::park(jlong millis) { int v ; for (;;) { v = _Event ; if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ; // cas设置_Event } if (v != 0) return OS_OK ; // os::PlatformEvent::unpark的时候会设置_Event=1,这里就会提前跳出 struct timespec abst; compute_abstime(&abst, millis); // 0. 计算绝对时间 int ret = OS_TIMEOUT; int status = pthread_mutex_lock(_mutex); // 1. 加mutex锁 // ... ++_nParked ; while (_Event < 0) { status = os::Linux::safe_cond_timedwait(_cond, _mutex, &abst); // 2. 等待 if (status != 0 && WorkAroundNPTLTimedWaitHang) { pthread_cond_destroy (_cond); pthread_cond_init (_cond, os::Linux::condAttr()) ; } if (!FilterSpuriousWakeups) break ; // previous semantics if (status == ETIME || status == ETIMEDOUT) break ; } --_nParked ; if (_Event >= 0) { ret = OS_OK; } _Event = 0 ; status = pthread_mutex_unlock(_mutex); // 3. 释放mutex锁 // ... return ret; }
Object.wait
wait 也是 JNI 方法
public final native void wait(long timeout) throws InterruptedException;
wait 一般是需要配合 synchrononized 使用的.入口是 JVM_MonitorWait
JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms)) JVMWrapper("JVM_MonitorWait"); Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); JavaThreadInObjectWaitState jtiows(thread, ms != 0); if (JvmtiExport::should_post_monitor_wait()) { JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms); } ObjectSynchronizer::wait(obj, ms, CHECK); JVM_END
wait 代码实现如下:
void ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) { if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); } if (millis < 0) { TEVENT (wait - throw IAX) ; THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative"); } ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj()); // 膨胀为重量级锁 DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), THREAD, millis); monitor->wait(millis, true, THREAD); // 调用wait dtrace_waited_probe(monitor, obj, THREAD); }
ObjectMonitor的wait函数 实现
void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { Thread * const Self = THREAD ; // ... if (interruptible && Thread::is_interrupted(Self, true) && !HAS_PENDING_EXCEPTION) { // ... THROW(vmSymbols::java_lang_InterruptedException()); // 处理中断 return ; } // ... AddWaiter (&node) ; // 1. 添加到ObjectMonitor的等待队列_WaitSet中 // ... exit (true, Self) ; // 2. 释放java的monitor锁(也就是monitorexit) // ... if (interruptible && (Thread::is_interrupted(THREAD, false) || HAS_PENDING_EXCEPTION)) { // Intentionally empty } else if (node._notified == 0) { if (millis <= 0) { Self->_ParkEvent->park () ; } else { ret = Self->_ParkEvent->park (millis) ; // 3. 等待,和Thread::sleep一样的 } } //... }
LockSupport.park
park 也是 JNI 方法
public native void park(boolean isAbsolute, long time);
Unsafe 实现
UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time)) // ... thread->parker()->park(isAbsolute != 0, time); // ... UNSAFE_END
park 方法实现, 调用的是 Parker 的park 函数,不是os:PlatformEvent::park
void Parker::park(bool isAbsolute, jlong time) { if (Thread::is_interrupted(thread, false)) { return; } timespec absTime; if (time < 0 || (isAbsolute && time == 0) ) { // don't wait at all return; } if (time > 0) { unpackTime(&absTime, isAbsolute, time); // 0. 计算绝对时间 } if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) { // 1. 尝试加mutex锁 return; } int status ; if (_counter > 0) { // no wait needed _counter = 0; status = pthread_mutex_unlock(_mutex); // 2.1 在park之前调用了unpark,就不会wait了 // ... return; } if (time == 0) { _cur_index = REL_INDEX; // arbitrary choice when not timed status = pthread_cond_wait (&_cond[_cur_index], _mutex) ; // 2.2 入参为0,一直等待 } else { _cur_index = isAbsolute ? ABS_INDEX : REL_INDEX; // 2.3 带超时的等待 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()); } } _counter = 0 ; status = pthread_mutex_unlock(_mutex) ; // 3. 释放mutex锁 }
可以看到 park 最终是依赖 pthread_cond_timedwait 方法实现阻塞。
LockSupport.park() 的实现原理是通过二元信号量做阻塞,unpark() 方法会释放一个许可证,park()方法则是获取许可证,要注意的是,无论执行多少次 unpark()方法,也最多只会有一个许可。