`
san_yun
  • 浏览: 2653210 次
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

JVM 对线程的实现

    博客分类:
  • java
 
阅读更多

在操作系统中,有两种不同的方法提供线程支持:用户层的用户线程,或内核层的内核线程。

其中用户线程在内核之上支持,并在用户层通过线程库来实现。不需要用户态/核心态切换,速度快。操作系统内核不知道多线程的存在,因此一个线程阻塞将使得整个进程(包括它的所有线程)阻塞。由于这里的处理器时间片分配是以进程为基本单位,所以每个线程执行的时间相对减少。

内核线程由操作系统直接支持。由操作系统内核创建、调度和管理。内核维护进程及线程的上下文信息以及线程切换。一个内核线程由于I/O操作而阻塞,不会影响其它线程的运行。

Java线程的实现是怎样的呢?我们通过SUN Java 6的源码了解其在Windows和Linux下的实现。

在Windows下的实现,os_win32.cpp中

<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->//  Allocate and initialize a new OSThread
bool  os::create_thread(Thread *  thread, ThreadType thr_type, size_t stack_size) {
  unsigned thread_id;

  
//  Allocate the OSThread object
  OSThread *  osthread  =   new  OSThread(NULL, NULL);
  
if  (osthread  ==  NULL) {
    
return   false ;
  }

  
//  Initial state is ALLOCATED but not INITIALIZED
  {
    MutexLockerEx ml(thread
-> SR_lock(), Mutex::_no_safepoint_check_flag);
    osthread
-> set_state(ALLOCATED);
  }
  
  
//  Initialize support for Java interrupts
  HANDLE interrupt_event  =  CreateEvent(NULL,  true false , NULL);
  
if  (interrupt_event  ==  NULL) {
    delete osthread;
    
return  NULL;
  }
  osthread
-> set_interrupt_event(interrupt_event);
  osthread
-> set_interrupted( false );
  
  thread
-> set_osthread(osthread);
  
  
if  (stack_size  ==   0 ) {
    
switch  (thr_type) {
    
case  os::java_thread:
      
//  Java threads use ThreadStackSize which default value can be changed with the flag -Xss
       if  (JavaThread::stack_size_at_create()  >   0 )
        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 ;
    }
  }

  
//  Create the Win32 thread
  
//
  
//  Contrary to what MSDN document says, "stack_size" in _beginthreadex()
  
//  does not specify stack size. Instead, it specifies the size of
  
//  initially committed space. The stack size is determined by
  
//  PE header in the executable. If the committed "stack_size" is larger
  
//  than default value in the PE header, the stack is rounded up to the
  
//  nearest multiple of 1MB. For example if the launcher has default
  
//  stack size of 320k, specifying any size less than 320k does not
  
//  affect the actual stack size at all, it only affects the initial
  
//  commitment. On the other hand, specifying 'stack_size' larger than
  
//  default value may cause significant increase in memory usage, because
  
//  not only the stack space will be rounded up to MB, but also the
  
//  entire space is committed upfront.
  
//
  
//  Finally Windows XP added a new flag 'STACK_SIZE_PARAM_IS_A_RESERVATION'
  
//  for CreateThread() that can treat 'stack_size' as stack size. However we
  
//  are not supposed to call CreateThread() directly according to MSDN
  
//  document because JVM uses C runtime library. The good news is that the
  
//  flag appears to work with _beginthredex() as well.

#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
#define  STACK_SIZE_PARAM_IS_A_RESERVATION  (0x10000)
#endif

  HANDLE thread_handle 
=
    (HANDLE)_beginthreadex(NULL,
                           (unsigned)stack_size,
                           (unsigned (__stdcall 
* )( void * )) java_start,
                           thread,
                           CREATE_SUSPENDED 
|  STACK_SIZE_PARAM_IS_A_RESERVATION,
                           
& thread_id);
  
if  (thread_handle  ==  NULL) {
    
//  perhaps STACK_SIZE_PARAM_IS_A_RESERVATION is not supported, try again
    
//  without the flag.
    thread_handle  =
    (HANDLE)_beginthreadex(NULL,
                           (unsigned)stack_size,
                           (unsigned (__stdcall 
* )( void * )) java_start,
                           thread,
                           CREATE_SUSPENDED,
                           
& thread_id);
  }
  
if  (thread_handle  ==  NULL) {
    
//  Need to clean up stuff we've allocated so far
    CloseHandle(osthread -> interrupt_event());
    thread
-> set_osthread(NULL);
    delete osthread;
    
return  NULL;
  }
  
  Atomic::inc_ptr((intptr_t
* ) & os::win32::_os_thread_count);

  
//  Store info on the Win32 thread into the OSThread
  osthread -> set_thread_handle(thread_handle);
  osthread
-> set_thread_id(thread_id);

  
//  Initial thread state is INITIALIZED, not SUSPENDED
  {
    MutexLockerEx ml(thread
-> SR_lock(), Mutex::_no_safepoint_check_flag);
    osthread
-> set_state(INITIALIZED);
  }

  
//  The thread is returned suspended (in state INITIALIZED), and is started higher up in the call chain
   return   true ;
}


可以看出,SUN JVM在Windows下的实现,使用_beginthreadex来创建线程,注释中也说明了为什么不用“Window编程书籍推荐使用”的CreateThread函数。由此看出,Java线程在Window下的实现是使用内核线程。

摘自<<windows操作系统原理>>
内核线程:由操作系统内核创建和撤销,内核维护进程及线程的上下文信息以及线程的切换,一个内核线程由于I/O操作而阻塞,
不会影响其他线程的运行,windows NT和2000 支持内核线程。
用户线程:由应用进程利用线程库创建和管理,不依赖操作系统的核心,不需要用户态/内核态的切换,速度快,操作系统内核不知道
多线程的存在,因此一个线程阻塞将使得整个进程(包括它的所有的线程)阻塞,由于这里的处理器时间片分配是以进程为基本单位的。所以每个线程执行的时间相对减少。


而在Linux下又是怎样的呢?

在os_linux.cpp文件中的代码摘录如下:

<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--># include <pthread.h>

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
         if  (JavaThread::stack_size_at_create()  >   0 ) 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);

    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 ;
}


Java在Linux下的线程的创建,使用了pthread线程库,而pthread就是一个用户线程库,因此结论是,Java在 Linux下是使用用户线程实现的。Linux 2.6内核的pthread实现为NPTL,和内核线程的映射是一对一。之前的Linux threads也是。

 

对于NPTL的一些介绍:

POSIX Thread Library (NPTL)使Linux内核可以非常有效的运行使用POSIX线程标准写的程序。这里有一个测试数据,在32位机下,NPTL成功启动100000个线程只用了2秒,而不使用NPTL将需要大约15分钟左右的时间。

 

历史


在内核2.6以前的调度实体都是进程,内核并没有真正支持线程。它是能过一个系统调用clone()来实现的,这个调用创建了一份调用进程 的拷贝,跟fork()不同的是,这份进程拷贝完全共享了调用进程的地址空间。LinuxThread就是通过这个系统调用来提供线程在内核级的支持的 (许多以前的线程实现都完全是在用户态,内核根本不知道线程的存在)。非常不幸的是,这种方法有相当多的地方没有遵循POSIX标准,特别是在信号处理, 调度,进程间通信原语等方面。

很显然,为了改进LinuxThread必须得到内核的支持,并且需要重写线程库。为了实现这个需求,开始有两个相互竞争的项目:IBM启动的 NGTP(Next Generation POSIX Threads)项目,以及Redhat公司的NPTL。在2003年的年中,IBM放弃了NGTP,也就是大约那时,Redhat发布了最初的 NPTL。

NPTL最开始在redhat linux 9里发布,现在从RHEL3起内核2.6起都支持NPTL,并且完全成了GNU C库的一部分。

 

设计

 

NPTL使用了跟LinuxThread相同的办法,在内核里面线程仍然被当作是一个进程,并且仍然使用了clone()系统调用(在NPTL库里调用)。但是,NPTL需要内核级的特殊支持来实现,比如需要挂起然后再唤醒线程的线程同步原语futex.

NPTL也是一个1*1的线程库,就是说,当你使用pthread_create()调用创建一个线程后,在内核里就相应创建了一个调度实体,在linux里就是一个新进程,这个方法最大可能的简化了线程的实现。

除NPTL的1*1模型外还有一个m*n模型,通常这种模型的用户线程数会比内核的调度实体多。在这种实现里,线程库本身必须去处理可能存在的调 度,这样在线程库内部的上下文切换通常都会相当的快,因为它避免了系统调用转到内核态。然而这种模型增加了线程实现的复杂性,并可能出现诸如优先级反转的 问题,此外,用户态的调度如何跟内核态的调度进行协调也是很难让人满意。

 

对于pthread的更多理解可以参考:http://archive.cnblogs.com/a/1930707/

分享到:
评论

相关推荐

    Java分布式应用学习笔记03JVM对线程的资源同步和交互机制

    ### Java分布式应用学习笔记03:JVM对线程的资源同步和交互机制 在深入探讨Java虚拟机(JVM)如何处理线程间的资源同步与交互机制之前,我们先来明确几个关键概念:线程、多线程、同步、并发以及它们在Java中的实现...

    JVM线程状态和Thread.sleep的实现原理探究.pdf

    在探究JVM线程状态以及Thread.sleep的实现原理时,我们首先需要了解Java线程与操作系统线程之间的关系。在Java虚拟机(JVM)中,每个线程通常都是以一对一的关系映射到操作系统线程上的。然而,尽管两者在实现上是...

    WAS性能调优对jvm、线程数、ORB大小的配置

    本文将深入探讨“WAS性能调优对jvm、线程数、ORB大小的配置”这一主题。 首先,我们要理解JVM(Java Virtual Machine)在WAS中的作用。JVM是Java程序运行的平台,它的性能直接影响到应用的响应时间和资源利用率。在...

    JVM+多线程.pdf

    HotSpot虚拟机上,方法区常被实现为永久代(PermGen),但随着JDK 1.8的推出,永久代被元空间(Metaspace)取代。 - **运行时常量池(Runtime Constant Pool)**:是方法区的一部分,存放编译期生成的各种字面量和...

    JVM监控实例数 windows监控 线程测试 单例模式下测试JVM实例是否一个

    为了确保应用程序高效稳定地运行,开发者需要对JVM进行监控,了解其内部状态,如内存使用、线程活动等。本文将探讨如何在Windows环境下通过简单的命令行工具来监控JVM实例的数量,以及如何进行线程测试,特别是在...

    揭秘Java虚拟机-JVM设计原理与实现

    7. **多线程**:JVM内置对多线程的支持,每个线程有自己的程序计数器、本地方法栈和虚拟机栈,共享堆和方法区。 8. **类文件结构**:深入理解`.class`文件的结构,包括魔数、版本号、常量池、字段表、方法表、属性...

    JVM线程分析.pdf

    总结来说,JVM线程分析主要涉及线程的状态管理,包括对Runnable、Wait on condition、Waiting for monitor entry和in Object.wait()的理解,以及如何利用Monitor进行线程同步。掌握这些知识对于理解和优化Java应用的...

    java 核心知识 包含 JVM 线程 集合 数据库 算法 负载等一系列

    本文将深入探讨Java的核心知识,包括JVM(Java虚拟机)、线程、集合、数据库、算法以及负载均衡等多个方面。 首先,让我们从Java虚拟机(JVM)开始。JVM是Java程序运行的基石,它负责解析字节码并执行。理解JVM的...

    JVM线程学习

    本资料集合旨在深入探讨JVM线程的相关知识。 首先,我们来看《Concurrent Programming in Java》这本书。这本书详细介绍了Java中的并发编程,包括JVM线程的创建、管理和同步。其中,线程的生命周期(新建、就绪、...

    jvmjava,java实现的JVM。.zip

    这有助于深入理解JVM的生命周期管理、异常处理、多线程等复杂概念。 五、优化与进阶 掌握JVM的工作原理后,开发者可以进行更高级的调优,如调整JVM参数以优化内存分配、提高垃圾收集效率等。此外,还可以探索JVM的...

    JVM Hotspot实现源码

    《OpenJDK中的JVM Hotspot实现源码解析》 在Java世界中,JVM(Java Virtual Machine)是运行Java程序的关键组件,它负责将字节码解释执行或即时编译为机器码,使得Java具备跨平台的能力。Hotspot是Oracle JDK和...

    JVM中的守护线程示例详解

    在Java虚拟机(JVM)中,线程分为两类:用户线程(User Thread)和守护线程(Daemon Thread)。这两类线程的主要区别在于它们的生命周期与JVM的关系。 1. **用户线程**:用户线程是应用程序中常见的线程类型,它们...

    JavaJVM线程调优.pdf

    总的来说,Java JVM线程调优需要综合考虑线程栈大小、锁优化策略以及并发同步方法,以适应不同的应用场景。理解这些概念并根据实际情况调整参数,可以显著提升多线程程序的性能。在进行调优时,应结合监控工具分析...

    HotSpot线程实现-线程创建1

    《HotSpot线程实现——线程创建》 在Java编程中,线程是我们处理并发和多任务的基础。本文将深入探讨HotSpot虚拟机中线程的实现,主要关注线程的创建过程。我们使用的JDK版本为1.7。请注意,本文不会涉及Java线程的...

    Jvm.rar_jni_jni 线程_jvm

    JNI的使用往往涉及到对JVM内部机制的了解,比如如何在本地代码中操作Java对象,管理Java的堆内存,以及处理线程同步问题。 "www.pudn.com.txt"这个文件可能是从网站www.pudn.com下载的资源说明或者示例代码的引用。...

    Java八股文之JVM与多线程

    Java虚拟机(JVM)是Java程序运行的基础,它提供了执行字节码...在面试中,理解JVM的工作原理、内存管理、垃圾回收机制以及多线程的实现与优化,不仅能展示出对Java底层机制的掌握,也是解决问题和提升系统性能的关键。

    推荐一些JVM原理,JVM调优,JVM内存模型,JAVA并发 电子书1

    Java内存模型(JMM)规定了线程对共享变量的可见性和有序性,它通过主内存和工作内存的概念来实现多线程之间的协作。 3. JVM调优:JVM调优通常指对JVM进行配置,优化性能以应对特定的应用需求。常见的调优手段包括...

    java中jvm原理和实现

    ### Java中JVM原理与实现 #### 一、引言 Java虚拟机(JVM)作为Java语言的基础支撑,是确保Java程序能够在不同平台上顺畅运行的关键技术。本文将深入探讨JVM的工作原理及其核心组成部分,包括类加载器、运行时数据区...

    jdk,jvm源码

    6. 多线程:JVM支持多线程并发执行,每个线程都有自己的程序计数器和虚拟机栈。 7. 安全管理:JVM执行严格的类型检查和权限控制,确保代码的安全性。 8. 调优工具:如JVisualVM、JConsole等,帮助开发者监控和调整...

    java进阶提高学习教程-13JVM与多线程.pptx

    Java进阶学习教程中的第13章主要涵盖了JVM(Java虚拟机)和多线程两个核心概念。JVM是Java程序运行的基础,它负责解释执行字节码文件,并提供了Java语言的跨平台特性。Java程序通过JVM与操作系统交互,使得程序可以...

Global site tag (gtag.js) - Google Analytics