java学习-并发-java.lang.Thread API

  java.lang.Thread API定义
    • new Thread 创建 Thread 对象
      • 对于当前操作并不涉及os线程的创建,只涉及java对象的创建
    • Thread#start 启动线程: 这里主要实现 jvm 和 java 代码通信(调用)实现系统线程的创建和执行
    • Thread#join 等待线程执行完成: 本质是通过一直探测jvm中c++的javaThread对象是否存在,如果存在表示当前线程执行还未结束,反之则代表结束,这里的核心点在于object#wait操作 
  public static void main(String[] args) throws InterruptedException {
        threadApi();
    }

    public static void threadApi() throws InterruptedException {
        /**
         * 在加载Thread.class 到jvm中时会执行当前Thread 类中的static代码块 以及 static变量
         * 此时就会执行 registerNatives 的native 方法对应的就是 thread.c 中的 Java_java_lang_Thread_registerNatives 方法;
         * 该方法的执行会将Thread中所有映射的native 方法和变量进行加载到jvm中
         */
        // 创建一个线程对象,对于这里的操作只涉及到java层面的对象创建,并不涉及os线程操作;对于其中的属性操作
        //  target -> 当前线程对象指定的runnable方法
        // group -> 线程组: 对于一个新的线程对象而言,默认会使用当前调用线程的所属线程组;
        // 对于stackSize 属性在@since 1.4 后就可以人工指定,而不需要只使用 xss的限制
        Thread thread = new Thread(() -> {
            log.info("java.lang.Thread api 学习");
        });

        // 启动线程 : thread#start
        /**
         *  start 首先是被synchronized 修饰表示当前方法操作是线程安全的同步操作: 因此就可以理解java language 规范中 happens-before 对于 start 和 run 的定义
         *  在start 中 首先会将当前线程对象添加到thread.threadGroup; 然后会调用 start0(native)
         *   对于start0是属于JNI方法,其对应的是jdk源码中的c++代码对应的就是"JVM_StartThread" 其对应的实现就是jvm.cpp中的 JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) 方法;
         *   "java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL"  对于 当前判断是通过判断当前jobject 是否已被实例化,如果已经被实例化说明当前对象已被创建表示对应的线程已被创建; 因此对于一个Thread对象而言如果调用了多次start方法则会抛出 异常"java_lang_IllegalThreadStateException"-> 对应的就是 "java.lang.IllegalThreadStateException" 在openjdk中 使用了 "THROW(vmSymbols::java_lang_IllegalThreadStateException());" 来抛出异常,其映射是存在于 vmSymbols.h中的模板定义"  template(java_lang_IllegalThreadStateException,     "java/lang/IllegalThreadStateException")    \"
         *
         *  "native_thread = new JavaThread(&thread_entry, sz);" 此时会创建 JavaThread 对象, 在这里传递了两个参数一个是 "thread_entry方法的引用(类似于java lambda)",第二个参数为当前线程栈大小
         *  对 "thread_entry"方法的分析:
         *      static void thread_entry(JavaThread* thread, TRAPS) {
         *   HandleMark hm(THREAD);
         *   Handle obj(THREAD, thread->threadObj()); // 该操作就是为了java Thread 对象
         *   JavaValue result(T_VOID);
         *   JavaCalls::call_virtual(&result, // 对于 call_virtual 实际就是执行的回调操作,该操作会调用java Thread#run 方法
         *                           obj,
         *                           SystemDictionary::Thread_klass(),
         *                           vmSymbols::run_method_name(), // 此处就是将对应的 run方法加入其中
         *                           vmSymbols::void_method_signature(),
         *                           THREAD);
         * }
         *  此时进入 JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) 构造方法
         *      1:执行当前JavaThread 对象的初始化操作:initialize()
         *      2:判断当前线程类型时 java_thread 还是 compiler_thread, 此时会调用 os::create_thread(Thread* thread, ThreadType thr_type, size_t req_stack_size)
         *  此时进入 os::create_thread(Thread* thread, ThreadType thr_type, size_t req_stack_size) (os_linux.cpp):
         *      在当前代码的关键点就在于 pthread api的使用,利用POSIX_THREAD高级api来创建os线程;
         *      对于 pthread_create第三个参数传递的 thread_native_entry 方法引用分析
         *          重点在于对于 "thread->call_run();"通过调用 "JavaThread::run()" -> "JavaThread::thread_main_inner()" -> "this->entry_point()(this, this);" (这里可以看上面"thread_entry"的解释) ->  该操作是正式执行Thread#run回调;
         *          当当前线程执行结束后会执行"JavaThread::post_run()" 进行资源回收
         *      对于当前方法而言,有可能由于内存不足导致线程创建失败,通过
         *      // Allocate the OSThread object
         *   OSThread* osthread = new OSThread(NULL, NULL);
         *   if (osthread == NULL) {
         *     return false;
         *   }
         *   来判断是否有空间创建线程对象;如果空间分配失败则表示线程创建失败,但其不会抛出OOM异常
         * 对于 以下代码分析:
         *  if (native_thread->osthread() != NULL) {
         *         // Note: the current thread is not being used within "prepare".
         *         native_thread->prepare(jthread);
         *       }
         *   osthread() 实际是调用的 "thread.hpp中的   OSThread* osthread() const                     { return _osthread;   }"
         *   对于 os::create_thread 中如果OSThread 对象创建成功则会调用 "void set_osthread(OSThread* thread)            { _osthread = thread; }",来设置 "_osthread" 属性,因此当对象OSThread 创建失败时,当前字段为null,因此可以通过当前字段判断线程是否真正创建成功
         */
        thread.start();
//        thread.start();// 抛出 IllegalThreadStateException

        /**
         * 阻塞等待当前线程执行结束
         * 这里主要是通过调用 native java.lang.Thread#isAlive() 实现,对应的c++ 中的操作就是去获取JavaThread 对象是否存在,对于 thread#start 中 在c++中有一个操作就是在post_run方法中会进行资源回收,因此当c++中javaThread执行结束后,对象就会被资源回收,代表当前线程已经执行结束;
         *
         * 这里还有一点关于 Object#wait native 分析: 对于wait操作实际是直接执行膨胀锁并不会有偏向锁的存在
         * 对于 Object#wait 对应的 jni 定义 存在于 jvm.h 的
         * JNIEXPORT void JNICALL
         * JVM_MonitorWait(JNIEnv *env, jobject obj, jlong ms);
         * 其具体实现为 jvm.cpp 中的 "JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))" 主要为 调用 "ObjectSynchronizer::wait(obj, ms, CHECK);"
         * 此时进入 synchronizer.cpp中的
         * int ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS)
         *  对于当前方法首先判断JVM是否支持偏向锁,如果支持偏向锁,"BiasedLocking::revoke(obj, THREAD);" 会对当前对象头执行偏向锁撤销操作
         * 后续会直接调用 "ObjectMonitor* ObjectSynchronizer::inflate(Thread* self, oop object,const InflateCause cause)" 进入膨胀过程
         *  对于膨胀的过程实际就是对 Inflated(已持有到锁) -> Stack-locked -> INFLATING -> Neutral -> BIASED 按照锁的粒度从高到低进行判断,最终转换为膨胀锁阶段 返回 ObjectMonitor;
         *  对于当前inflate实际就是 object转换为 ObjectMonitor的过程
         *  对于 ObjectMonitor分析 : 其中存在一个 waitSet 属性值, 其操作就是类似于 AQS 的双向队列
         *  ObjectWaiter* volatile _WaitSet;  // LL of threads wait()ing on the monitor
         *  对于 ObjectWaiter 也可以等价于 java.util.concurrent.locks.AbstractQueuedSynchronizer.Node
         * 再次进入 objectMonitor.cpp中的
         * void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS)
         *  会首先创建一个ObjectWaiter 节点准备加入到等待队列中,会使用当前Thread的parkevent 执行reset进行重置挂起操作,使用OrderAccess.fence建立内存屏障
         *  对于addWaiter 操作也是受到 spinlock的保护,因此需要 在入队之前先执行 Thread::SpinAcquire 操作 对于当前入队操作 和 java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire(java.util.concurrent.locks.AbstractQueuedSynchronizer.Node, int, boolean, boolean, boolean, long) 操作类似, 不同点在于 对于 c++是直接使用了 spinLock;
         *  对于 spinLock acquire 首先会执行cas操作作为fast,如果失败,则执行下一个阶段, 在此阶段如果为单核则直接阻塞当前线程; 反之则 会首先执行 线程 yield ,并执行cas操作如果操作持续失败,且yield次数达到阈值,则会执行线程休眠1ms操作,并直到cas操作成功,则加锁成功;
         *  在执行AddWaiter 操作中 和 aqs node acquire不同点在于初始化链表时并不会创建一个无意义的头节点; 释放锁
         * 调用 Thread#ParkEvent属性 的park 方法 实际 就是 os::PlatformEvent::park 内部使用了 POSIXThread中的相关的lock以及condition 相关api
         *
         */
        thread.join();

        /**
         * 分析 jdk.internal.misc.Unsafe#park(boolean, long)
         * 对应的就是 unsafe.cpp中的 "UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time))" 其最终操作也是执行到了os::PlatformEvent::park 内部使用了 POSIXThread中的相关的lock以及condition 相关api
         */
    }

    public static void executors() {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        // 由于 core = 2, 当前只提交了一个任务,因此会理解创建任务,并创建新的thread对象,并调用start 方法来执行当前线程的runnable
        executorService.submit(() -> {
            log.info("线程启动");
        });
        executorService.shutdown();
    }

对于这里大量的操作实际都是使用了 POSIX_Thread 高级api相关操作

 

Thread.start0() -> JVM_StartThread : JVM Native 调用
JVM_StartThread -> new JavaThread() : 创建一个 JavaThread() 对象
new JavaThread() -> os_create_thread() : 创建线程(OS 相关的)
os_create_thread() -> pthread_create() : 创建 POSIX Thread
pthread_create() -> java_start() : 线程启动并回调 java_start()
java_start() -> JavaThread_run() : 调用 JavaThread run() 方法
JavaThread_run() -> thread_main_inner() : 内部调用
thread_main_inner() -> entry_point() : entry_point 指向 thread_entry()
thread_entry() -> java.lang.Thread.run() : 回调 Java Thread run() 方法

java.lang.Object#wait(long) -> JVM_MonitorWait -> ObjectSynchronizer::wait -> "Self->_ParkEvent->park ()" -> os::PlatformEvent::park() -> OS pthread_cond_wait

Unsafe#park(boolean,long) -> JVM Native ->  JVM Parker::park -> OS pthread_cond_wait

Unsafe#unpark() -> JVM Native ->  JVM Parker::unpark -> OS pthread_cond_singal

posted @ 2020-10-16 11:29  郭星  阅读(136)  评论(0)    收藏  举报