Java Thread类解析(结合Java源码与openjdk源码)

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
waitnotify()/notifyAll()WAITING
sleep休眠时间结束立即唤醒TIMED_WAITING
parkunpark()WAITING
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值