Thread类本地方法注册流程及映射关系
Thread类在类初始化阶段第一件事就是调用registerNatives()注册start0(),interrupt0()等本地方法。
private static native void registerNatives();
static {
registerNatives();
}
registerNatives()方法在虚拟机中将通过Java_java_lang_Thread_registerNatives注册Thread类的所有本地方法。(比如Thread的start0()方法在虚拟机中实际执行的是JVM_StartThread方法)。
/* 下面代码位于openjdk源码openjdk\jdk\src\share\native\java\lang\Thread.c中 */
static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
{"isAlive", "()Z", (void *)&JVM_IsThreadAlive},
{"suspend0", "()V", (void *)&JVM_SuspendThread},
{"resume0", "()V", (void *)&JVM_ResumeThread},
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
{"yield", "()V", (void *)&JVM_Yield},
{"sleep", "(J)V", (void *)&JVM_Sleep},
{"currentThread", "()" THD, (void *)&JVM_CurrentThread},
{"countStackFrames", "()I", (void *)&JVM_CountStackFrames},
{"interrupt0", "()V", (void *)&JVM_Interrupt},
{"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted},
{"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock},
{"getThreads", "()[" THD, (void *)&JVM_GetAllThreads},
{"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
{"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};
JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
start()源码,start0()openjdk源码
以下是start()及方法源码,start()方法在调用start0()方法前将检查线程状态,同时将线程加入线程组当中,最后调用本地start0()方法正式创建并启动线程。
/**
* 使该线程开始执行;Java虚拟机调用该线程的run方法。
* 调用start后将有两条线程正在执行,主线程从start()返回执行后面的代码,子线程同时执行run()方法中的代码
* 同一线程调用2次start()是非法的,特殊情况下一个线程可以在执行完毕后重新开始
*
public synchronized void start() {
/* 通过sun.misc.VM.toThreadState(threadStatus)从虚拟机获取当前线程状态,新建线程的状态应为0 */
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* 将子线程加入线程组,通知线程组该线程即将启动,并减少线程组的未启动数 */
group.add(this);
boolean started = false;
try {
/*调用本地方法start0()*/
start0();
started = true;
} finally {
try {
/*如果线程启动失败通知线程组*/
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* 不做任何处理,如果start0执行失败线程会被传递到堆中 */
}
}
}
/*调用虚拟机的JVM_StartThread方法*/
private native void start0();
下一步虚拟机会根据start0()在虚拟机对应的JVM_StartThread方法的宏定义创建一个名为native_thread的本机线程:
//下面代码位于openjdk源码openjdk\hotspot\src\share\vm\prims\jvm.cpp中
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
......
//检查stillborn的值是否正常,正常会返回一个大于0的值
jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
//通过c++线程线程框架创建本机线程
size_t sz = size > 0 ? (size_t) size : 0;
//关键代码,创建操作系统线程
native_thread = new JavaThread(&thread_entry, sz);
......
创建native_thread时调用JavaThread构造方法有两个参数,第二个参数sz为线程中已有线程的数量,第一个参数thread_entry方法里面就包括了run方法回调的宏定义:vmSymbols::run_method_name()。
//下面代码位于openjdk源码openjdk\hotspot\src\share\vm\prims\jvm.cpp中
static void thread_entry(JavaThread* thread, TRAPS) {
HandleMark hm(THREAD);
Handle obj(THREAD, thread->threadObj());
JavaValue result(T_VOID);
JavaCalls::call_virtual(&result,
obj,
KlassHandle(THREAD, SystemDictionary::Thread_klass()),
vmSymbols::run_method_name(),
vmSymbols::void_method_signature(),
THREAD);
}
native_thread调用的有参构造源码如下,关键在于执行os::create_thread创建操作系统线程
//下面代码位于openjdk源码openjdk\hotspot\src\share\vm\runtime\Thread.cpp中
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
Thread()
......
// Create the native thread itself.
// %note runtime_23
os::ThreadType thr_type = os::java_thread;
thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :os::java_thread;
//关键代码:创建操作系统线程
os::create_thread(this, thr_type, stack_sz);
......
接下来看虚拟机的Thread::start方法,实际调用了操作系统开始线程的os::start_thread方法
void Thread::start(Thread* thread) {
trace("start", thread);
......
os::start_thread(thread);
......
}
其他方法
线程主体:run()
Oracle官方文档的解释是:如果线程使用一个单独的Runnable对象构造,那么该Runnable的run方法将被调用,否则,此方法不执行任何操作并返回。其实就是线程执行业务代码的地方。
停止线程:stop()
此方法已停用,要终止线程并不推荐使用stop方法,因为结果可能会导致数据不同步。
中断线程:interrupt(),isInterrupted(),interrupted()
interrupt()常与isInterrupted()搭配使用,可以修改线程标识为中断状态,但是线程依然会继续执行。而interrupted()是一个静态方法,调用的是currentThread().isInterrupted(true),isInterrupted(true)会在判断线程标识状态前重置状态。
需要注意的是当中断线程时线程处于睡眠状态时会抛出InterruptedException,这时需要在catch代码块里再次interrupt()才能正确中断线程。
public static void main(String[] args) {
Thread t=new Thread(){
@Override
public void run(){
while(!Thread.currentThread().isInterrupted()){
System.out.println("1");
// try {
// Thread.sleep(500l);
// } catch (Exception e) {
// interrupt();
// e.printStackTrace();
// }
}
}
};
t.start();
try {
Thread.sleep(3000l);
} catch (Exception e) {
e.printStackTrace();
}
t.interrupt();
}
睡眠:sleep(long millis)
作用:持有对象的锁并等待millis毫秒后再继续线程。
让步:yield()
作用:是的当前线程让出CPU资源,重新和其他就绪状态的线程争夺CPU资源。
例子:启动10个子线程对index加加,主线程等待子线程执行完毕获取index的值。
static int index=0;
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<10;i++){
new Thread(()->{
index++;
}).start();
}
while(Thread.activeCount()>2){
Thread.yield();
}
System.out.println(index);
}
等死:join()
作用:等待当前线程死亡。
例子:启动10个子线程对index加加,主线程等待子线程执行完毕获取index的值。
static int index=0;
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<10;i++){
Thread t=new Thread(()->{
index++;
});
t.start();
t.join();
}
System.out.println(index);
}
等待、唤醒:wait(),nofity(),notifyAll()
wait():释放对象的锁,并使当前线程进入阻塞等待。
nofity():唤醒当前线程组内随机一条线程,使其获得对象的锁。
nofityAll():尝试唤醒当前线程组内所有线程,使其竞争对象的锁。
例子:同时创建并启动2个线程,线程1每1秒向容器添加1个元素,直到添加10个结束,线程2在线程1往容器中添加第5个元素的时候自动停止。
代码如下:(使用wait(),与notify()解决)
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
//线程2
new Thread(() -> {
System.out.println("thread2 start");
synchronized (list) {
//如果容器长度不为5主动进入等待
if (list.size() != 5) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//唤醒线程1,由于线程组中除主线程和线程2外只有线程1,所以必定能唤醒线程1
list.notify();
}
System.out.println("thread2 end");
}).start();
new Thread(() -> {
synchronized (list) {
//遍历添加10个元素
for (int i = 0; i < 10; i++) {
list.add("添加元素" + i);
System.out.print("--add" + (i + 1)+"--");
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
//添加第5个元素时主动进入等待并唤醒线程2
if (list.size() == 5) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.notify();
}
}
}).start();
}
执行结果如下:
获取当前线程:currentThread()
用法:一般在run方法中使用,获取该线程对象。
public static void main(String[] args){
new Thread(new Runnable(){
@Override
public void run(){
//在线程内打印线程名
System.out.println(Thread.currentThread().getName());
}
},"Thread1").start();
}
设置守护进程:setDaemon(boolean on)
用法:在线程创建之后,start之前使用。
作用:当主线程及所有子线程都停止后,守护进程自动停止。
public class ThreadTest {
int num=0;
public void f1(int i){
synchronized (this) {
while(true){
num+=i;
System.out.println(Thread.currentThread().getName()+"---"+num);
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(num==5)num=num/0;
}
}
}
public static void main(String[] args) throws InterruptedException{
ThreadTest st=new ThreadTest();
new Thread(()->st.f1(1),"t1").start();
Thread.sleep(1000l);
Thread t2=new Thread(()->st.f1(1000),"t2");
t2.setDaemon(true);
t2.start();
}
}
运行结果如下图:t1抛出线程后t2作为守护进程自动中断
问题总结
Object.wait(),Thread.sleep(mills)和LockSupport.park()区别
唤醒方式 | 线程状态 | 是否释放锁 | 是否需要捕获InterruptedException | |
---|---|---|---|---|
wait | notify()/notifyAll() | WAITING | 是 | 是 |
sleep | 休眠时间结束立即唤醒 | TIMED_WAITING | 否 | 是 |
park | unpark() | WAITING | 否 | 否 |