jvm 实现多线程,本质上是调用所在平台系统的接口创建多线程或者多进程。比如windows上,jvm调用_beginthreadex创建线程,这是内核线程。对于Linux,Linux从内核2.6开始使用NPTL (Native POSIX Thread Library)支持,但这时线程本质上还轻量级进程。其和核心线程一一对应,但也算是内核线程。 因为是内核线程直接提供支持和对应创建,销毁。
Java里的线程是由JVM来管理的,它如何对应到操作系统的线程是由JVM的实现来确定的。Linux 2.6上的HotSpot使用了NPTL机制,JVM线程跟内核轻量级进程有一一对应的关系。线程的调度完全交给了操作系统内核,当然jvm还保留一些策略足以影响到其内部的线程调度,举个例子,在linux下,只要一个Thread.run就会调用一个fork产生一个线程。
Java线程在Windows及Linux平台上的实现方式,现在看来,是内核线程的实现方式。这种方式实现的线程,是直接由操作系统内核支持的——由内核完成线程切换,内核通过操纵调度器(Thread Scheduler)实现线程调度,并将线程任务反映到各个处理器上。内核线程是内核的一个分身。程序一般不直接使用该内核线程,而是使用其高级接口,即轻量级进程(LWP),也即线程。这看起来可能很拗口。看图:
(说明:KLT即内核线程Kernel Thread,是“内核分身”。每一个KLT对应到进程P中的某一个轻量级进程LWP(也即线程),期间要经过用户态、内核态的切换,并在Thread Scheduler 下反应到处理器CPU上。)
这种线程实现的方式也有它的缺陷:在程序面上使用内核线程,必然在操作系统上多次来回切换用户态及内核态;另外,因为是一对一的线程模型,LWP的支持数是有限的。毕竟占用内核空间,给调度器带来困难。不多对于多核CPU,我们可以适当的放宽内核线程数目。对于内核线程出现堵塞,那么内核将要将线程挂起,那么线程就会从内核状态切换到用户状态,这也是很耗资源的。
对于有些平台或者程序完全是建立在用户线程上的(有的数据库多线程),也就是内核里面只对应一个内核线程,但是上层多线程的同步,切换需要完全由开发者设计和实现。(UT User thread)这当然也需要系统的用户线程的提供接口支持。
当然有的程序设计是基于内核线程和用户线程混合的模式,在基于Unix平台上的几款虚拟机就是这样设计的。
可见对于jvm多线程的实现仅仅从线程的角度就分好几种(基于线程,有的多线程是基于进程如PHP)。
确立多核并发系统或者多CPU系统线程合适数目:
对于一个大型程序,我们可以开辟的线程数量至少等于运行机器的cpu内核数量。java程序里我们可以通过下面的一行代码得到这个数量:
Runtime.getRuntime().availableProcessors();所以最小线程数量即时cpu内核数量。如果所有的任务都是计算密集型的,这个最小线程数量就是我们需要的线程数。开辟更多的线程只会影响程序的性能,因为线程之间的切换工作,会消耗额外的资源。