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

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

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

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

在Windows下的实现,os_win32.cpp中
// 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, truefalse, 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下的实现是使用内核线程。

而在Linux下又是怎样的呢?

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

# 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下是使用用户线程实现的。





温少 2007-04-18 05:28 发表评论
posted on 2007-04-18 05:28 温少的日志 阅读(919) 评论(4)  编辑  收藏
Comments
  • # re: 关于Java线程的实现
    Tony Xu
    Posted @ 2007-04-19 11:27
    不敢苟同“因此一个线程阻塞将使得整个进程(包括它的所有线程)阻塞”,Linux用户线程和内核线程是1:1对应的  回复  更多评论   
  • # re: 关于Java线程的实现
    温少的日志
    Posted @ 2007-04-22 03:57
    其实偶不是很清楚Linux下pthread的最终实现,看过资料,说pthread是用户线程库。

    在solaris下,pthread的用户线程和内核线程影射,是多对多的。

    Windows下Fiber也可以实现多对多。

    linux下的实现,我了解还不多,2.6在线程支持方面有重大变化,但偶还没花时间去学习。  回复  更多评论   
  • # re: 关于Java线程的实现
    温少的日志
    Posted @ 2007-04-22 11:45
    在Linux下使用Firefox、Eclipse等软件,当某个操作阻塞的时候,整个程序甚至整个Gnome桌面会停止响应。

    这一现象和用户线程出现阻塞的现象吻合。

    使用Ubuntu工作一段时间了,说实在,没有在Windows XP下流畅。

    同一台机器上,Eclipse跑在Windows XP下的性能比Ubuntu LInux下快多了,在Linux下,经常出现某个操作阻塞时,整个Eclipse失去响应,而在Windows下,很少出现这种情况。  回复  更多评论   
  • # re: 关于Java线程的实现
    larz
    Posted @ 2007-04-25 06:20
    在linux下使用使用firefox、eclipse感到缓慢、回应性差的主要原因,在于底层的gtk+和cairo的效能问题,这点在firefox的使用者群组已经被提出很多次,我推测使用gtk+绘制介面的eclipse也是相同原因。  回复  更多评论   

只有注册用户登录后才能发表评论。


网站导航: