Java与线程
导语
我们知道,new一个thread,调用它的start的方法,就可以创建一个线程,并且启动该线程,然后执行该线程需要执行的业务逻辑,
那么run方法是怎么被执行的呢?
Java线程和os线程
os线程
我们知道,java的一个线程实际上是对应了操作系统的一个线程;
而操作系统实现线程有三种方式:
- 内核线程实现
- 用户线程实现
- 用户线程加轻量级进程混合实现
内核线程实现

简要说明:
内核线程(Kernel-Level Thread简称KLT)是直接由操作系统内核直接支持的线程,这种线程由内核完成线程切换,内核通过操纵调度器对线程进行调度,
并负责将线程的任务映射到各个处理器上。
每个内核线程可以视为内核的一个分身,这样操作系统就有能力处理多件事情,这种支持多线程的内核就叫做多线程内核;
程序一般不会直接操作内核线程,而是去使用内核线程的一种高级接口:轻量级进程(Light Weight Process,简称LWP),轻量级进程就是我们通常所讲的
线程,每个轻量级进程都由一个内核线程支持,这种轻量级进程与内核线程1:1的关系称为一对一的线程模型。
优势:由于有了内核线程的支持,每个轻量级进程都称为一个独立的调度单元,即使有一个轻量级进程在系统中阻塞了,也不会影响整个进程继续工作;
劣势:
- 由于基于内核线程实现,所以各种线程操作(创建,析构及同步等)都需要进行系统调用(系统调用代价相对较高,需要在用户态[User Mode]和内核态[Kernel Mode]来回切换);
- 每个轻量级进程需要一个内核线程支持,因此需要消耗一定的内核资源(如内核线程的栈空间),因此一个系统支持轻量级进程的数量是有限的;
用户线程实现

简要说明:
用户线程:
- 广义上来讲,任何非内核线程都可以认为是用户线程(User Thread,简称UT);
- 狭义上来讲,完全建立在用户空间的线程库上,系统内核不能感知线程存在的实现;
特点:
- 用户线程的创建、同步、销毁和调度完全在用户态中完成,不需要内核的帮助,这种线程不需要切换到内核态,操作可以非常快速且低消耗;
- 支持更大的线程数量;
- 这种进程与用户线程之间1:N的关系称为一对多的线程模型;
用户线程的优势就是不需要系统内核支援,劣势就是没有内核支持,所有的线程操作(如线程的创建、切换和调度等)都需要用户程序自己处理。
用户线程加轻量级进程混合实现

简要说明:
用户线程+轻量级进程特点:
- 用户线程的创建、析构、切换等操作很廉价;
- 支持大规模的用户线程并发;
- 操作系统提供的轻量级进程作为用户线程和内核线程之间的桥梁,提供线程调度功能及处理器映射;
- 用户线程的系统调用通过轻量级进程完成,降低了整个进程完全被阻塞的风险;
- 这种用户线程和轻量级进程的数量比不定,即N:M的关系,称为多对多的线程模型
Java线程
Java线程在JDK1.2之前,是基于用户线程实现的。而在JDK1.2中,线程模型替换为基于操作系统原生线程模型来实现。
而在目前的JDK版本中,操作系统支持怎样的线程模型,在很大程度上决定了Java虚拟机的线程是怎样映射的,这点在不同的平台上没法达成一致。
对于Sun JDK来说,它的Windows版本和Linux版本都是使用一对一的线程模型实现的,一条Java线程映射到一条轻量级进程之中。
Java线程创建
创建方式

Desc:我们看到,无论以哪种方式创建,最终我们都会重写一个叫做 run 的方法,来处理我们的业务逻辑,然而我们都是调用一个start方法,来启动一个线程;
那 start方法和run方法之间是一个什么关系呢?从后边的介绍我们将获得这样一个信息:run就是一个回调函数,和我们普通的函数没有区别。
Java线程的实现
一个 Java 线程的创建本质上就对应了一个本地线程(native thread)的创建,两者是一一对应的。
关键问题是:本地线程执行的应该是本地代码,而 Java 线程提供的线程函数(run)是 Java 方法,编译出的是 Java 字节码,
所以, Java 线程其实提供了一个统一的线程函数,该线程函数通过 Java 虚拟机调用 Java 线程方法 , 这是通过 Java 本地方法调用来实现的。
以下是 Thread#start 方法的示例:

可以看到它实际上调用了本地方法 start0, 而start0声明如下:
private native void start0();
也就是新创建的线程启动调用native start0方法,而这些native方法的注册是在Thread对象初始化的时候完成的,look:

Thread 类有个 registerNatives 本地方法,该方法主要的作用就是注册一些本地方法供 Thread 类使用,如 start0(),stop0() 等等,可以说,所有操作本地线程的本地方法都是由它注册的。
这个方法放在一个 static 语句块中,当该类被加载到 JVM 中的时候,它就会被调用,进而注册相应的本地方法。
而本地方法 registerNatives 是定义在 Thread.c 文件中的。Thread.c 是个很小的文件,它定义了各个操作系统平台都要用到的关于线程的公用数据和操作,如下:
JNIEXPORT void JNICALL
Java_Java_lang_Thread_registerNatives (JNIEnv *env, jclass cls){ //registerNatives
(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
static JNINativeMethod methods[] = {
{"start0", "()V",(void *)&JVM_StartThread}, //start0 方法
{"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},
};
观察上边一小段代码,可以容易的看出 Java 线程调用 start->start0 的方法,实际上会调用到 JVM_StartThread 方法,那这个方法又是怎么处理的呢?
实际上,我们需要看到的是该方法最终要调用 Java 线程的 run 方法,事实的确也是这样的。
在 jvm.cpp 中,有如下代码段:
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)){
...
native_thread = new JavaThread(&thread_entry, sz);
...
}
static void thread_entry(JavaThread* thread, TRAPS) {
HandleMark hm(THREAD);
Handle obj(THREAD, thread->threadObj());
JavaValue result(T_VOID);
JavaCalls::call_virtual(&result,obj,
KlassHandle(THREAD,SystemDictionary::Thread_klass()),
vmSymbolHandles::run_method_name(), //LOOK! 看这里
vmSymbolHandles::void_method_signature(),THREAD);
}
可以看到调用了 vmSymbolHandles::run_method_name 方法,而run_method_name是在 vmSymbols.hpp 用宏定义的:
class vmSymbolHandles: AllStatic {
...
template(run_method_name,"run") //LOOK!!! 这里决定了调用的方法名称是 “run”!
...
}
Java线程创建调用关系

Java与线程的更多相关文章
- java之线程
java之线程 一:线程: 线程是什么呢?线程,有时被称为轻量级进程是程序执行流的最小单元.一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成.另外,线程是进程中的一个实体,是被系统 ...
- Java 使用线程方式Thread和Runnable,以及Thread与Runnable的区别
一. java中实现线程的方式有Thread和Runnable Thread: public class Thread1 extends Thread{ @Override public void r ...
- Java的线程安全
线程安全 我们这里讨论的线程安全,就限定于多个线程之间存在共享数据访问这个前提,因为如果一段代码根本不会与其他线程共享数据,那么从线程安全的角度来看,程序是串行执行还是多线程执行对它来说是完全没有区别 ...
- 深入理解Java之线程池
原作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本文归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则 ...
- java中线程分两种,守护线程和用户线程。
java中线程分为两种类型:用户线程和守护线程. 通过Thread.setDaemon(false)设置为用户线程: 通过Thread.setDaemon(true)设置为守护线程. 如果不设置次属性 ...
- java 多线程—— 线程让步
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- java 多线程—— 线程等待与唤醒
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- Java的线程模型
并发不一定要依赖多线程(如PHP中很常见的多进程并发),但是在Java里面谈论并发,大多数都与线程脱不开关系. 线程是比进程更轻量级的调度执行单位,线程的引入,可以把一个进程的资源分配和执行调度分开, ...
- Java多线程 - 线程状态
转自: http://www.cnblogs.com/lwbqqyumidi/p/3804883.html 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的 ...
- Java Thread线程控制
一.线程和进程 进程是处于运行中的程序,具有一定的独立能力,进程是系统进行资源分配和调度的一个独立单位. 进程特征: A.独立性:进程是系统中独立存在的实体,可以拥有自己独立的资源,每个进程都拥有自己 ...
随机推荐
- Rxlifecycle(三):坑
坑1 Observable.just("hello world!") .compose(this.<String>bindUntilEvent(ActivityEven ...
- iOS CoreData 增删改查详解
最近在学习CoreData, 因为项目开发中需要,特意学习和整理了一下,整理出来方便以后使用和同行借鉴.目前开发使用的Swift语言开发的项目.所以整理出来的是Swift版本,OC我就放弃了. 虽然S ...
- [原]OpenGL基础教程(二)多边形绘制
上篇介绍了最基本的三角形绘制,本篇介绍如何使用索引的方式绘制多边行. 为什么要使用索引方式,总体来说提高性能.如何提高:使用顶点数组的好处是避免大量的函数调用.即避免每画一个顶点就调用1次glVert ...
- Hadoop 2.4.1 登录认证配置小结
1.简单模式 这种模式,配置简单,使用简单. core-site.xml添加 <property> <name>hadoop.security.authorization< ...
- glow
原则是: 先把原场景渲染到fbo,然后渲染发光的物体 然后叠加,但是问题来了,发光物体是另外一个fbo里渲染的,他没和原场景进行深度测试,导致全部绘制了,叠到一起的时候原先不该显示的部分显示 然后我立 ...
- WPF国际化(多语言)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.W ...
- 国内的maven镜像
阿里云 <mirror> <id>alimaven</id> <name>aliyun maven</name> <url>ht ...
- Android odex文件反编译
odex 是经过优化的dex文件,且独立存在于apk文件.odex 多用于系统预制应用或服务.通过将apk中的dex文件进行 odex,可以加载 apk 的启动速度,同时减小空间的占用.请参考ODEX ...
- Ubuntu 配置AP总结
1.这个是使用别人写的一个GUI来配置,:http://hi.baidu.com/lexiangtaotao/item/5d4e87f22db132c70cd1c86f 2.使用hostapd配置:h ...
- JavaScript备忘录(2)——闭包
语句 JavaScript是解释型语言,解释器是按照顺序逐句执行的(除了进行一些少量预处理,如将函数声明提前). 顺序是由流程控制语句来控制的,常用的流程控制语句包括: 条件控制语句:if...els ...