Java虚拟机之Java与线程

并发不一定要依赖多线程,也可以是多进程。但是在java中,并发一般是线程相关。本文主要是从java虚拟机的角度来探讨线程的实现。

线程的实现

我们都知道在操作系统层面来讲,线程是比进程更轻量级的调度执行单位,在计算机中,线程是CPU执行调度的基本单位。进程可以产生线程,所以线程的引入,把进程的资源分配和执行调度实现分离,各个线程既可以共享线程的资源,比如内存地址,文件IO等,又可以独立调度执行任务作业(如前文所说,线程是CPU最小调度单元)。
现在主流的操作系统都实现了线程。不同的平台不同的操作系统,对线程的实现原理可能都会有差别。java线程基于不同的平台和操作系统,做了统一的封装处理,从语言层面来看,各个平台和操作系统并无差异。
目前实现线程主要有3种方式:

  • 使用内核线程实现
  • 使用用户线程实现
  • 使用用户线程加轻量级进程实现

使用内核线程实现

内核线程就是操作系统内核直接支持的线程,由操作系统内核完成线程切换,内核通过操作调度器实现对线程的执行调度,并将线程任务映射到不同的处理器上。看起来内核就像一个操作系统的分身,这样操作系统就可以实现同时处理多个任务的可能。
在实现上,现在程序一般不会值直接调用内核线程,而是调用轻量级进程,轻量级进程和内核线程一对一映射。先有支持内核线程,才能有轻量级进程。这种轻量级进程和内核线程之间1:1的关系称为一对一的线程模型。由于是操作系统支持多线程,所以在某个任务使用一个线程执行的时候遇到了阻塞,一般也不会影响其他线程的处理。其原理如下图:

image.png

由于每个轻量级进程与内核线程一一对应,所以每个轻量级进程都是一个独立的调度单元。
其优点是每个轻量级进程相互不影响,一个阻塞不会影响其他轻量级进程。
其缺点也是非常明显,由于是内核线程,那么线程所有的操作如创建、析构、同步都需要在内核态完成,需要频繁的在用户态和内核态切换,同时需要轻量级进程和内核线程一一对应,也会消耗内核线程的栈空间。

使用用户线程实现

从广义上来讲,只要不是内核线程,就可以认为是用户线程。如果从这个角度来看,轻量级进程也算是用户态线程。而从狭义上来讲,完全建立在用户空间的线程库上,系统内核完全感知不到用户线程库的存在。像线程的创建、同步、销毁和调度等等工作全部在用户态完成。
其实现原理如下图:

image.png

如上图,可知一个进程对应多个用户线程,这种进程与用户线程1:N的关系称为一对多的线程模型
由于所有的线程操作与内核无关,都是在用户空间完成,所以其优缺点也很明显。
其优点是:线程所有操作都在用户态完成,不需要切换到内核态,如果实现得当,那么速度非常快,资源消耗也很低,可以支持更大规模的线程数量。
其缺点是由于线程的操作没有操作系统内核的支援,所有线程操作都需要用户程序自己实现,创建、线程间的切换、调度都需要用户程序处理,并且由于操作系统只把处理器资源分配到了进程,除了线程切换获取处理器外,线程阻塞如何处理,或者把进程内的线程映射到其他处理器,都是非常困难的。
现在使用用户线程的程序越来越少了,像Java、Ruby等语言都曾经使用过用户线程,不过现在都放弃了。

使用用户线程加轻量级进程混合实现

线程除了依赖内核实现和完全使用用户程序实现,还可以将内核线程与用户线程线程一起使用来实现。既存在用户线程,也存在轻量级进程,用户线程负责完全建立在用户控件,线程的创建、切换、析构、销毁等操作依然速度快,消耗资源少,而线程的调度,多线程和多处理器之间的映射则由轻量级进程对应的内核线程来处理,降低了线程调度和阻塞的风险。但是此时,用户线程和轻量级进程之间的数量是不确定的,即N:M的关系,这种模型称为多对多的线程模型
其原理如下:

image.png

Java线程的实现

Java线程在JDK1.2之前,是基于成为“绿色线程”的用户线程实现的,从JDK1.2开始,线程模型替换为使用操作系统原生的线程模型来实现。所以操作系统支持什么样的线程模型,java虚拟机的线程模型就是什么样的。在Linux和Windows系统中,由于系统本身的使用的一对一的线程模型,所以java虚拟机的线程也是一对一的,一个java线程映射到一个轻量级进程。而Solaris系统既支持一对一的线程模型(BoundThread),也支持多对多的线程模型(LWP/Thread Based Synchronization),所以Solaris版的JDK也提供了虚拟机参数-XX:+UseLWPSynchronization(默认值)和-XX:+UseBoundThreads来指定虚拟机使用哪种线程模型。

Java线程调度

线程调度是指操作系统为线程分配处理器使用权的过程。目前主要的调度方式有两种:协同式线程调度(Cooperation Threads-Scheduling)和抢占式线程调度(Preemptive Threads-Scheduling)。

  • 协同式线程调度
    协同式线程调度,线程的执行时间由线程自己决定,线程执行完成后就通知操作系统,我执行完成,可以切换到其他线程来执行。
    优点是实现简单,并且线程调度切换对线程自身是可知的,只有执行完成才会切换,线程间频繁切换消耗的资源在这里就没有了。
    缺点是线程执行时间不可控,容易造成其他线程一直阻塞或得不到执行。从而产生雪崩效应,整个系统崩溃。
  • 抢占式线程调度
    抢占式线程调度,线程的执行是由操作系统来分配决定的,线程间的切换不是由线程自身决定的。
    优点是线程的执行时间可控,由操作系统决定,并且每个线程都有执行的机会,一旦某个线程阻塞不会影响其他线程。
    缺点是线程间频繁的切换,需要对当前正在执行的线程相关上下文进行保存,恢复等,消耗资源。
    在抢占式线程调度方式中,虽然操作系统决定哪个线程执行。但是我们可以告诉操作系统某些线程希望获取执行的时间多一些,机会大一些。这就是我们常说的线程优先级。在java中,一共设置了10个线程优先级Thread.MIN_PRIORITY到Thread.MAX_PRIORITY,当不同线程同时处于就绪状态后,优先级大的获得执行的概率就大。但是这并不是绝对的,因为java使用的是操作系统的原生线程模型,而不同的操作系统对线程的优先级定义跟java不同,如Solaris定义了2^32次方个优先级,Windows定义了7个线程优先级。如果操作系统线程优先级比java的多,那还好,中间空余好多优先级,java的线程优先级会一一对应的不同的系统定义的线程优先级上,但是如果比java的线程优先级少,那是比会有不同的java线程优先级映射到相同的系统定义的线程优先级上。同时,操作系统也会有“优先级推进器”,即发现某个线程执行的非常勤奋努力,那么就会越过县城优先级的设置,尽可能多的去给该线程分配执行时间。

Java线程状态转换

操作系统的进程(线程),从最经典的就绪、执行、阻塞三种状态,到后来为了管理等增加了新建、挂起、结束等状态。而Java语言定义的线程状态有5中分别是:

  • 新建(New):创建后尚未启动的线程处于这种状态
  • 运行(Running):运行态就是启动后的线程,即调用了start()方法的线程,该状态对应到操作系统就是就绪和运行两种状态,即要么是正在运行的线程,要么是除了没有分配cpu,其他资源都有经获取完成的线程。
  • 等待(Waiting):等待状态的线程分为无限期等待(Waiting)和有限期等待(Timed Waiting)。无限期等待不会被分配cpu执行时间,需要等待其他线程显示的唤醒。如调用了:没有设置Timeout参数的Object.wait()方法,没有设置Timeout的Thread.join()方法,LockSupport.park()方法。有限期等待也不会被分配cpu执行时间,不过不是被其他线程显示唤醒,而是等待一定的时间后系统自动唤醒。如调用了:Thread.sleep()方法,设置了Timeout参数的Object.wait()方法,设置了Timeout参数的Thread.join方法,LockSupport的pardNanos()和parkUnit()方法。
  • 阻塞(Blocked):阻塞状态是线程在等待获取一个排它锁,这个事件只有其他线程释放这个排它锁的时候发生,线程在等待进入同步区域的时候就是出于阻塞状态
  • 结束(Terminated):执行结束的线程状态。
    这五种线程状态在遇到某些特定事件后会相互转换,如下图:


    image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容