linux c 线程的睡眠,从Java到JVM到OS线程睡眠

Java 中有时需要将线程进入睡眠状态,这时一般我们就会通过 Thread.sleep 使线程进入睡眠状态,接下去就看看执行该语句在 JVM 中做了什么。

简单例子

以下是一个简单的例子,使主线程睡眠5秒钟。

public class TestSleep {

public static void main(String[] args) {

try {

Thread.sleep(5000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

JVM 中的线程

在继续往 JVM 层看 start0 本地方法前,我们先了解下 JVM 中的相关线程,这将有助于后面更好理解 Java 层线程与 JVM 中的线程对应关系。

在 JVM 中,也用 C++ 定义了一些 Thread 类,它们的继承结构如下,其中对于 Java 层线程到 JVM 层主要相关的有 Java 层的 java.lang.Thread、JavaThread 和 OSThread。

java.lang.Thread 属于 Java 层的线程对象,每个 Java 层对象都会在 JVM 中使用 oop 来表示,所以它也会在 JVM 中产生一个 oop。

Thread 是 C++ 定义的线程基类,除了 OSThread 类,作为其他线程的基类,它包含了 OSThread 对象的指针。

JavaThread 是 C++ 定义的线程类,我们在 Java 层创建的线程对象会使用 JavaThread 对象来表示,它包含了指向线程的 oop 的指针。

OSThread 是 C++ 定义的线程,它不与其他线程构成继承关系,它是 JVM 对不同操作系统的线程的统一抽象,它维护了操作系统线程的句柄,用于获取操作系统的线程。

--Thread

--JavaThread

--CodeCacheSweeperThread

--CompilerThread

--JvmtiAgentThread

--ServiceThread

--NamedThread

--ConcurrentGCThread

--VMThread

--WorkerThread

--AbstractGangWorker

--GCTaskThread

--WatcherThread

--OSThread

sleep方法

在 Thread 类中, sleep 是一个静态且本地方法。

public static native void sleep(long millis) throws InterruptedException;

Thread.c

Java 层声明的本地方法对应实现在 Thread.c 中, sleep 是一个注册到 JVM 中的方法,它与 JVM 中的 JVM_Sleep 函数绑定了,所以实现逻辑在 JVM_Sleep 函数里。逻辑为:

JVMWrapper("JVM_Sleep") 用于调试。

睡眠时间不能为负。

是否已经被中断了。

JavaThreadSleepState jtss(thread) 用于修改线程状态并做一些统计,当睡眠结束后,会修改回线程状态,在 JavaThreadSleepState 的析构函数中修改。

睡眠时间如果为0,则根据 ConvertSleepToYield 做不同处理,它表示是否将 sleep 操作转为 yield 操作。分别调用 os::naked_yield 和 os::sleep 处理,封装了不同操作系统的调用实现,后面以 Windows 为例分别看相应实现。

通过 thread->osthread()->get_state() 获取 OSThread 对象,并将其状态设置为 SLEEPING等到 sleep 结束后设置回原来的状态。

如果睡眠时间大于0,则做类似操作,不过它支持中断。

发送事件,结束。

os::naked_yield

naked_yield 函数的实现很简单,就直接调用 SwitchToThread 系统函数。通过该函数可以让系统查看是否有其他线程迫切需要CPU,将CPU让给其他线程,如果没有其他线程则立即返回。

void os::naked_yield() {

SwitchToThread();

}

JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))

JVMWrapper("JVM_Sleep");

if (millis 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) {

if (!HAS_PENDING_EXCEPTION) {

if (event.should_commit()) {

event.set_time(millis);

event.commit();

}

HOTSPOT_THREAD_SLEEP_END(1);

THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");

}

}

thread->osthread()->set_state(old_state);

}

if (event.should_commit()) {

event.set_time(millis);

event.commit();

}

HOTSPOT_THREAD_SLEEP_END(0);

JVM_END

os::sleep

获取最大限制大小limit。

如果超过 limit 则通过减法将其转成多次递归调用 sleep 函数。

获取 OSThread 对象,然后通过 OSThreadWaitState 设置线程状态为等待,修改操作分别在构造函数和析构函数中实现。

根据是否支持中断做不同实现,不需要中断则直接调用 Sleep 系统函数来实现。

如果要支持中断则接着做下面处理。

ThreadBlockInVM 主要是检查当前线程用不用进入 safepoint,后面再详细看。

接着主要到 WaitForMultipleObjects 系统函数,该函数能等待指定对象指定的毫秒数。如果等待过程中对象没有接到任何信号,则超过指定毫秒数后返回 WAIT_TIMEOUT ,如果等待过程中对象收到信号,则提前解除等待,此时返回的值为 OS_INTRPT ,即表示被中断了。

int os::sleep(Thread* thread, jlong ms, bool interruptable) {

jlong limit = (jlong) MAXDWORD;

while (ms > limit) {

int res;

if ((res = sleep(thread, limit, interruptable)) != OS_TIMEOUT) {

return res;

}

ms -= limit;

}

assert(thread == Thread::current(), "thread consistency check");

OSThread* osthread = thread->osthread();

OSThreadWaitState osts(osthread, false /* not Object.wait() */);

int result;

if (interruptable) {

assert(thread->is_Java_thread(), "must be java thread");

JavaThread *jt = (JavaThread *) thread;

ThreadBlockInVM tbivm(jt);

jt->set_suspend_equivalent();

HANDLE events[1];

events[0] = osthread->interrupt_event();

HighResolutionInterval *phri=NULL;

if (!ForceTimeHighResolution) {

phri = new HighResolutionInterval(ms);

}

if (WaitForMultipleObjects(1, events, FALSE, (DWORD)ms) == WAIT_TIMEOUT) {

result = OS_TIMEOUT;

} else {

ResetEvent(osthread->interrupt_event());

osthread->set_interrupted(false);

result = OS_INTRPT;

}

delete phri;

jt->check_and_wait_while_suspended();

} else {

assert(!thread->is_Java_thread(), "must not be java thread");

Sleep((long) ms);

result = OS_TIMEOUT;

}

return result;

}

ThreadBlockInVM

前面说到 ThreadBlockInVM 会检查当前线程用不用进入 safepoint,它主要的逻辑如下:

首先设置 Java 线程状态,将状态加一,由 _thread_in_vm = 6 变为 _thread_in_vm_trans = 7,从“运行vm本身代码”到“相应的过度状态”。

os::is_MP() 用于判断计算机系统是否为多核系统,多核情况下需要做内存屏障处理,这是为了让每个线程都能实时同步状态。

内存屏障有两种方式,一种是 rderAccess::fence() ,它的实现是直接通过CPU指令来实现,汇编指令为 asm volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory"); ,这种方式代价比较大。而另外一种为 InterfaceSupport::serialize_memory ,由 JVM 模拟实现,效率高一点。

调用 SafepointSynchronize::block 尝试在该安全点进行阻塞。

设置 Java 线程状态为 _thread_blocked ,即阻塞。

static inline void transition_and_fence(JavaThread *thread, JavaThreadState from, JavaThreadState to) {

assert(thread->thread_state() == from, "coming from wrong thread state");

assert((from & 1) == 0 && (to & 1) == 0, "odd numbers are transitions states");

thread->set_thread_state((JavaThreadState)(from + 1));

if (os::is_MP()) {

if (UseMembar) {

OrderAccess::fence();

} else {

// Must use this rather than serialization page in particular on Windows

InterfaceSupport::serialize_memory(thread);

}

}

if (SafepointSynchronize::do_call_back()) {

SafepointSynchronize::block(thread);

}

thread->set_thread_state(to);

CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops();)

}

0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值