• java的多线程是如何实现的?和操作系统有什么关系?


    本文是作者原创,版权归作者所有.若要转载,请注明出处.本文只贴我觉得比较重要的源码,其他不重要非关键的就不贴了

    本文操作系统是centos7

     1.查看 pthread_create 函数显示及其示例

    man pthread_create

     2.cengos下安装man手册命令:

     yum install man-pages -y

     3.重新查看 pthread_create 函数显示及其示例

    man pthread_create

    如下图

    根据man配置的信息可以得出pthread_create会创建一个线程,这个函数是linux系统的函 数,可以用C或者C++直接调用,

    上面信息也告诉程序员这个函数在pthread.h, 这个函数 有四个参数

    一个小demo

    #include <pthread.h>//头文件
     #include <stdio.h>
     pthread_t pid;//定义一个变量,接受创建线程后的线程id
     //定义线程的主体函数
     void* thread_entity(void* arg)
     {
     printf("i am new Thread!");
     }
     //main方法,程序入口,main和java的main一样会产生一个进程,继而产生一个main线程
     int main()
     {
     //调用操作系统的函数创建线程,注意四个参数
     pthread_create(&pid,NULL,thread_entity,NULL);
     //usleep是睡眠的意思,那么这里的睡眠是让谁睡眠呢?
     //为什么需要睡眠?如果不睡眠会出现什么情况
     usleep(100);
     printf("main
    ");
     }

    4.那么java当中的线程和操作系统的线程是什么关系呢?,我们看下例子

    public static void main(String[] args) {
            Thread thread = new Thread(){
                @Override
                public void run() {
                    System.out.println("i am new Thread!");
                }
            };
            thread.start();
        }

    上述例子可以看出:我们启用线程是调用start方法,而非直接调用run方法

    5.我们追踪start方法的源码:

    public synchronized void start() {
            /**
             * This method is not invoked for the main method thread or "system"
             * group threads created/set up by the VM. Any new functionality added
             * to this method in the future may have to also be added to the VM.
             *
             * A zero status value corresponds to state "NEW".
             */
            if (threadStatus != 0)
                throw new IllegalThreadStateException();
    
            /* Notify the group that this thread is about to be started
             * so that it can be added to the group's list of threads
             * and the group's unstarted count can be decremented. */
            group.add(this);
    
            boolean started = false;
            try {
                start0();//这行是关键代码
                started = true;
            } finally {
                try {
                    if (!started) {
                        group.threadStartFailed(this);
                    }
                } catch (Throwable ignore) {
                    /* do nothing. If start0 threw a Throwable then
                      it will be passed up the call stack */
                }
            }
        }

    6.继续追踪我注释的这行关键代码start0()

    private native void start0();

    这个是native方法,具体实现需要查看openjdk的源码

    7.openjdk的写jni一般是一一对应的,Thread.java对应的就是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},
    };

    8.start0其实就是JVM_StartThread。在jvm.h中找到了声明

    /*
     * java.lang.Thread
     */
    JNIEXPORT void JNICALL
    JVM_StartThread(JNIEnv *env, jobject thread);

    9.在jvm.cpp中有实现,如下

    JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
      JVMWrapper("JVM_StartThread");
      JavaThread *native_thread = NULL;
    
      // We cannot hold the Threads_lock when we throw an exception,
      // due to rank ordering issues. Example:  we might need to grab the
      // Heap_lock while we construct the exception.
      bool throw_illegal_thread_state = false;
    
      // We must release the Threads_lock before we can post a jvmti event
      // in Thread::start.
      {
        // Ensure that the C++ Thread and OSThread structures aren't freed before
        // we operate.
        MutexLocker mu(Threads_lock);
    
        // Since JDK 5 the java.lang.Thread threadStatus is used to prevent
        // re-starting an already started thread, so we should usually find
        // that the JavaThread is null. However for a JNI attached thread
        // there is a small window between the Thread object being created
        // (with its JavaThread set) and the update to its threadStatus, so we
        // have to check for this
        if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
          throw_illegal_thread_state = true;
        } else {
          // We could also check the stillborn flag to see if this thread was already stopped, but
          // for historical reasons we let the thread detect that itself when it starts running
    
          jlong size =
                 java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
          // Allocate the C++ Thread structure and create the native thread.  The
          // stack size retrieved from java is signed, but the constructor takes
          // size_t (an unsigned type), so avoid passing negative values which would
          // result in really large stacks.
          size_t sz = size > 0 ? (size_t) size : 0;
          native_thread = new JavaThread(&thread_entry, sz);//关键代码
    
          // At this point it may be possible that no osthread was created for the
          // JavaThread due to lack of memory. Check for this situation and throw
          // an exception if necessary. Eventually we may want to change this so
          // that we only grab the lock if the thread was created successfully -
          // then we can also do this check and throw the exception in the
          // JavaThread constructor.
          if (native_thread->osthread() != NULL) {
            // Note: the current thread is not being used within "prepare".
            native_thread->prepare(jthread);
          }
        }
      }
    
      if (throw_illegal_thread_state) {
        THROW(vmSymbols::java_lang_IllegalThreadStateException());
      }
    
      assert(native_thread != NULL, "Starting null thread?");
    
      if (native_thread->osthread() == NULL) {
        // No one should hold a reference to the 'native_thread'.
        delete native_thread;
        if (JvmtiExport::should_post_resource_exhausted()) {
          JvmtiExport::post_resource_exhausted(
            JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
            "unable to create new native thread");
        }
        THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
                  "unable to create new native thread");
      }
    
      Thread::start(native_thread);
    
    JVM_END

    10.大概浏览上下文,native_thread的构造应该是在JavaThread的构造中完成的.再看new JavaThread(...)这步

    JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
      Thread()
    #if INCLUDE_ALL_GCS
      , _satb_mark_queue(&_satb_mark_queue_set),
      _dirty_card_queue(&_dirty_card_queue_set)
    #endif // INCLUDE_ALL_GCS
    {
      if (TraceThreadEvents) {
        tty->print_cr("creating thread %p", this);
      }
      initialize();
      _jni_attach_state = _not_attaching_via_jni;
      set_entry_point(entry_point);
      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);//这里创建线程
      _safepoint_visible = false;
    
    }

    11.到了os::create_thread这步,具体实现在os_linux.cpp中(这里我们只说linux的,其他系统的实现就不在这里了)

    bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
      assert(thread->osthread() == NULL, "caller responsible");
    
      // Allocate the OSThread object
      OSThread* osthread = new OSThread(NULL, NULL);
      if (osthread == NULL) {
        return false;
      }
    
      // set the correct thread state
      osthread->set_thread_type(thr_type);
    
      // Initial state is ALLOCATED but not INITIALIZED
      osthread->set_state(ALLOCATED);
    
      thread->set_osthread(osthread);
    
      // init thread attributes
      pthread_attr_t attr;
      pthread_attr_init(&attr);
      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    
      // stack size
      if (os::Linux::supports_variable_stack_size()) {
        // calculate stack size if it's not specified by caller
        if (stack_size == 0) {
          stack_size = os::Linux::default_stack_size(thr_type);
    
          switch (thr_type) {
          case os::java_thread:
            // Java threads use ThreadStackSize which default value can be
            // changed with the flag -Xss
            assert (JavaThread::stack_size_at_create() > 0, "this should be set");
            stack_size = JavaThread::stack_size_at_create();
            break;
          case os::compiler_thread:
            if (CompilerThreadStackSize > 0) {
              stack_size = (size_t)(CompilerThreadStackSize * K);
              break;
            } // else fall through:
              // use VMThreadStackSize if CompilerThreadStackSize is not defined
          case os::vm_thread:
          case os::pgc_thread:
          case os::cgc_thread:
          case os::watcher_thread:
            if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K);
            break;
          }
        }
    
        stack_size = MAX2(stack_size, os::Linux::min_stack_allowed);
        pthread_attr_setstacksize(&attr, stack_size);
      } else {
        // let pthread_create() pick the default value.
      }
    
      // glibc guard page
      pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));
    
      ThreadState state;
    
      {
        // Serialize thread creation if we are running with fixed stack LinuxThreads
        bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();
        if (lock) {
          os::Linux::createThread_lock()->lock_without_safepoint_check();
        }
    
        pthread_t tid;
        int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);//看这行代码,是不是就是linux系统的线程调用函数
    
        pthread_attr_destroy(&attr);
    
        if (ret != 0) {
          if (PrintMiscellaneous && (Verbose || WizardMode)) {
            perror("pthread_create()");
          }
          // Need to clean up stuff we've allocated so far
          thread->set_osthread(NULL);
          delete osthread;
          if (lock) os::Linux::createThread_lock()->unlock();
          return false;
        }
    
        // Store pthread info into the OSThread
        osthread->set_pthread_id(tid);
    
        // Wait until child thread is either initialized or aborted
        {
          Monitor* sync_with_child = osthread->startThread_lock();
          MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);
          while ((state = osthread->get_state()) == ALLOCATED) {
            sync_with_child->wait(Mutex::_no_safepoint_check_flag);
          }
        }
    
        if (lock) {
          os::Linux::createThread_lock()->unlock();
        }
      }
    
      // Aborted due to thread limit being reached
      if (state == ZOMBIE) {
          thread->set_osthread(NULL);
          delete osthread;
          return false;
      }
    
      // The thread is returned suspended (in state INITIALIZED),
      // and is started higher up in the call chain
      assert(state == INITIALIZED, "race condition");
      return true;
    }

    12.这里终于看到了系统库函数的调用。,看我注释的那行代码,是不是就是linux系统的线程调用函数pthread_create

    调用线程执行的方法是java_start,参数是thread

    看下源码

    // Thread start routine for all newly created threads
    static void *java_start(Thread *thread) {
      // Try to randomize the cache line index of hot stack frames.
      // This helps when threads of the same stack traces evict each other's
      // cache lines. The threads can be either from the same JVM instance, or
      // from different JVM instances. The benefit is especially true for
      // processors with hyperthreading technology.
      static int counter = 0;
      int pid = os::current_process_id();
      alloca(((pid ^ counter++) & 7) * 128);
    
      ThreadLocalStorage::set_thread(thread);
    
      OSThread* osthread = thread->osthread();
      Monitor* sync = osthread->startThread_lock();
    
      // non floating stack LinuxThreads needs extra check, see above
      if (!_thread_safety_check(thread)) {
        // notify parent thread
        MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
        osthread->set_state(ZOMBIE);
        sync->notify_all();
        return NULL;
      }
    
      // thread_id is kernel thread id (similar to Solaris LWP id)
      osthread->set_thread_id(os::Linux::gettid());
    
      if (UseNUMA) {
        int lgrp_id = os::numa_get_group_id();
        if (lgrp_id != -1) {
          thread->set_lgrp_id(lgrp_id);
        }
      }
      // initialize signal mask for this thread
      os::Linux::hotspot_sigmask(thread);
    
      // initialize floating point control register
      os::Linux::init_thread_fpu_state();
    
      // handshaking with parent thread
      {
        MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
    
        // notify parent thread
        osthread->set_state(INITIALIZED);
        sync->notify_all();
    
        // wait until os::start_thread()
        while (osthread->get_state() == INITIALIZED) {
          sync->wait(Mutex::_no_safepoint_check_flag);
        }
      }
    
      // call one more level start routine
      thread->run();//终于找到run方法了
    
      return 0;
    }

    13.这个方法可以直接跳到最后一行,他执行的是传递过来对象的run方法

    14.总结:由上面源码可以看到:Java的多线程是直接调用linux的多线程函数实现的

    每个继承java.lang.Thread的类,调用start方法之后,都调用start0()的native方法;

    start0()的native方法在openjdk里调用的是JVM_StartThread;

    JVM_StartThread最终调用的是操作系统的pthread_create()函数,有四个参数,我们写的run方法就是该函数的第三个参数.

  • 相关阅读:
    在mvc中,使用a链接,怎么转到别的html中
    mvc中怎么读取值传到后台中方法之一(表单传值法)
    mvc中怎么带参传递
    sqlserver去掉字符串结尾的全角空格并用半角替换
    Ajax学习笔记
    Ajax级联实例
    [转]js导航栏处于选中状态
    asp.net GridView的使用
    keycode大全
    IsPostBack的使用
  • 原文地址:https://www.cnblogs.com/lusaisai/p/12729334.html
Copyright © 2020-2023  润新知