深入理解JVM(③)线程与Java的线程
前言
我们都知道,线程是比进程更轻量级的调度执行单位,线程的引入,可以把一个进程的资源分配和执行调度分开,各个线程既可以共享进程资源调度(内存地址、文件I/O等),又可以独立调度。
线程的实现
主流的操作系统都提供了线程实现,Jav语言则是提供了在不同硬件和操作系统平台下对线程操作的统一处理,每个已经调用过start()方法且还未结束的java.lang.Thread
类的实例就代表这一个线程。
其实Thread类与大部分的Java类库API有着显著差别,它的所有关键方法都被声明为Native。在Java中,一个Native方法往往就意味着这个方法没有使用或无法使用平台无关的手段来实现(通常最高效率的手段就是平台相关的手段)。
那么线程的实现其实是有三种方式的:
- 使用内核线程实现(1:1实现);
- 使用用户线程实现(1:N)实现;
- 使用用户线程加轻量级进程混合实现;
内核线程实现
使用内核线程实现的方式被称为1:1实现。内核线程(Kernel Levvel Thread,KLT)就是直接由操作系统内核(Kernel,下称内核)支持的线程,内核通过操纵调度器(Scheduler)对线程进行调度,并负责将线程的任务映射到各个处理器上。
其实程序一般不会直接使用内核线程,而是使用内核线程的一种高级接口——轻量级进程(Light Weight Process,LWP),轻量级进程就是我们通常所讲的线程。这种轻量级进程与内存线程之间1:1的关系称为一对一的线程模型。
轻量级进程也具有它的局限性:首先,由于是基于内核线程实现的,所以各种线程操作(创建、析构及同步),都需要进行系统调用。系统调用就要在用户态和内核态中来回切换。其次,每个轻量级进程都需要一个内核线程的支持,因此需要消耗一定的内核资源,所以一个系统支持轻量级进程的数量是有限的。
用户线程实现
使用用户线程实现的方式被称为1:N实现。广义上来讲,一个线程只要不是内核线程,都可以任务是用户线程(User Threa,UT)的一种。从定义上来看轻量级进程不是内核线程也就是属于用户线程,但是它始终是建立在内核之上的,所以效率会受到限制,并不具备用户线程的优点。
用户线程的建立、同步、销毁和调度完全咋用户态中完成,不需要内核帮助。如果程序实现得当,不需要切换内核态,因此操作可以是非常快且低消耗的,也能够支持规模更大的线程数量,部分高性能数据库中的多线程就是由用户线程实现的。
这种进程与用户线程之间1:N的关系称为一对多的线程模型。
用户线程的速度快低消耗等优势在于不需要系统内核支援,但是劣势也在于没有内核的支援,所有的线程操作都需要由用户程序自己去处理。这样就会导致线程的一些问题处理起来就很困难,甚至有些是不可能实现的。
Java、Ruby等予以都曾经使用过用户线程,最终又都放弃了使用它。
混合实现
线程除了依赖内核线程实现和完全由用户程序自己实现之外,还有一种将内核线程与用户线程一起使用的实现方式,被称为N:M实现。
用户线程还是完全建立在用户空间中,因此用户线程的创建、切换、析构等操作依然廉价,并且可以支持大规模的用户线程并发。而操作系统支持的轻量级进程则作为用户线程和内核线程之间的桥梁,这样可以使用内核提供的线程调度功能及处理器映射,并且用户线程的系统调用要通过轻量级进程来完成,大大降低了整个进程被完全阻塞的风险。
Java线程的实现
Java线程如何实现并不受Java虚拟机规范约束,这是一个与具体虚拟机相关的画图。Java线程在早期的Classic虚拟机上(JDK1.2以前),是基于一种被称为“绿色线程”(Green Threads)的用户线程实现的,但从JDK1.3起,“主流”平台上的“主流”商用Java虚拟机的线程模型普遍都被替换为基于操作系统原生线程模型来实现,即采用1:1的线程模型。
操作系统支持怎样的线程模型,在很大程度想会影响上面的Java虚拟机的线程是怎么样映射的,这一点咋不同的平台上很难达成一致,因此《Java虚拟机规范》中才不去限定Java线程需要使用哪种线程模型来实现。
Java线程调度
线程调度是指系统为线程分配处理使用权的过程,调度主要方式有两种,分别是协同式(Cooperative Threads-Scheduling)线程调度和抢占式(Preemptive Threads-Scheduling)线程调度。
- 协同式线程调度:线程的执行时间由线程本身来控制,线程把自己的工作执行完了之后,要主动通知系统切换到另外一个线程上去。
优点:实现简单,切换操作对线程自己是可知的,所以一般没有什么线程同步问题。
缺点:线程执行时间不可控制,甚至如果一个线程的代码编写有问题,一直不告知系统进行线程切换,那么程序就会一直阻塞在那里。 - 抢占式线程调度:每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定。
优点:可以主动让出执行时间(例如Java的Thread::yield()
方法),并且线程的执行时间是系统可控的,也不会有一个线程导致整个系统阻塞的问题。
缺点:无法主动获取执行时间。
Java使用的就是抢占式线程调度,虽然这种方式的线程调度是系统自己的完成的,但是我们可以给操作系统一些建议,就是通过设置线程优先级来实现。Java语言一共设置了10个级别的线程优先级。在两个线程同时处于Ready状态时,优先级越高的线程越容易被系统选择执行。
不过由于各个系统的提供的优先级数量不一致,所以导致Java提供的10个级别的线程优先级并不见得能与各系统的优先级都一一对应。
Java线程状态转换
Java语言定义了6种线程状态,在任意一个时间点钟,一个线程只能有且只有其中的一种状态,并且可以通过特定的方法在不同状态之间切换。
- 新建(New):创建后尚未启动的线程处于这种状态。
- 运行(Runnable):包括操作系统线程状态中的Running和Ready,也就是处理此状态的线程有可能正在执行,也有可能正在等待着操作系统为它分配执行时间。
- 无限期等待(Waiting):处于这种状态的线程不会被分配处理器执行时间,它们要等待被其他线程显示唤醒。
以下方法会让线程陷入无限期等待状态:
1、没有设置Timeout参数的Object::wait()
方法;
2、没有设置Timeout参数的Thread::join()
方法;
3、LockSupport::park()
方法。 - 限期等待(Timed Waiting):处于这种状态的线程也不会被分配处理器执行时间,不过无须等待被其他线程显式唤醒,在一定时间之后它们会由系统自动唤醒。
以下方法会让线程进入限期等待状态:
1、Thread::sleep()
方法;
2、设置了Timeout参数的Object::wait()
方法;
3、设置了Timeout参数的Thread::join()
方法;
4、LockSupport::parkNanos()
方法;
5、LockSupport::parkUntil()
方法; - 阻塞(Blocked):线程被阻塞了,“阻塞状态”与“等待状态”的区别是“阻塞状态”在等待着获取到一个排他锁,这个事件将在另外一个线程放弃这个锁的时候发生;而“等待状态”则是在等待一段时间 ,或者唤醒动作发生。在程序进入同步区域的时候,线程将进入这种状态。
- 结束(Terminated):已终止线程的线程状态,线程已经结束执行。
这6种状态在遇到特定事件发生的时候将会互相转换,他们的转换关系如下图:
深入理解JVM(③)线程与Java的线程的更多相关文章
- 从 JVM 视角看看 Java 守护线程
Java 多线程系列第 7 篇. 这篇我们来讲讲线程的另一个特性:守护线程 or 用户线程? 我们先来看看 Thread.setDaemon() 方法的注释,如下所示. Marks this thre ...
- 深入理解JVM(③)再谈线程安全
前言 我们在编写程序的时候,一般是有个顺序的,就是先实现再优化,并不是所有的牛P程序都是一次就写出来的,肯定都是不断的优化完善来持续实现的.因此我们在考虑实现高并发程序的时候,要先保证并发的正确性,然 ...
- 【深入理解JVM】:Java对象的创建、内存布局、访问定位
对象的创建 一个简单的创建对象语句Clazz instance = new Clazz();包含的主要过程包括了类加载检查.对象分配内存.并发处理.内存空间初始化.对象设置.执行ini方法等. 主要流 ...
- 【深入理解JVM】:Java内存模型JMM
多任务和高并发的内存交互 多任务和高并发是衡量一台计算机处理器的能力重要指标之一.一般衡量一个服务器性能的高低好坏,使用每秒事务处理数(Transactions Per Second,TPS)这个指标 ...
- 深入理解JVM(③)Java的锁优化
前言 从JDK5到JDK6HotSpot虚拟机开发团队花费了大量的资源实现了各种锁优化技术,如适应性自旋(Adaptive Spinning).锁消除(Lock Elimination).锁膨胀(Lo ...
- 外部线程停止Java子线程的方法
一.Thread.stop()官方不推荐,Because it is inherently unsafe. 二.方式一1. 线程类示例 public class ThreadT1 implements ...
- 【深入理解JVM】:Java类继承关系中的初始化顺序
尝试着仔细阅读thinking in java 看到一篇很好的文章http://blog.csdn.net/u011080472/article/details/51330114
- 深入理解JVM(③)Java模块化系统
前言 JDK9引入的Java模块化系统(Java Platform Module System ,JPMS)是 对Java技术的一次重要升级,除了像之前JAR包哪有充当代码的容器之外,还包括: 依赖其 ...
- 深入理解JVM(③)Java的模块化
前言 JDK9引入的Java模块化系统(Java Platform Module System ,JPMS)是 对Java技术的一次重要升级,除了像之前JAR包那样充当代码的容器之外,还包括: 依赖其 ...
随机推荐
- MySQL数据库离线包安装与注册
本文主要介绍了MySQL数据库的离线安装和将MySQL服务注册为Windows应用服务的主要步骤. 1.下在安装程序包 MySQL Community Server 5.6.15 官方下载地址http ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(五)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
- 解决:gradle 前言中不允许有内容
将Android Studio 升级到4.0然后创建一个新项目,编译出现“ gradle 前言中不允许有内容” 的错误,在网上找了很多资料,众说纷纭,但都没有解决我的问题,最后反复摸索把问题解决了. ...
- 基于httpclient的一些常用方法封装
package com.util; import java.io.IOException; import java.io.UnsupportedEncodingException; import ja ...
- 用python复制文件夹
用python复制文件 1. 根据文件夹的名称复制 需要复制的文件夹编号文件中,每一行表示一个编号,如下所示: > cat id.txt 1 2 3 ... > 目标文件的目录结构树如下所 ...
- DML_Data Modification_MERGE
DML_8-Data Modification_MERGE (将Source表合并到Target) 语法:MERGE INTO 目标表USING 源表WHEN MATCHED AND ...
- 机器学习中的标准化方法(Normalization Methods)
希望这篇随笔能够从一个实用化的角度对ML中的标准化方法进行一个描述.即便是了解了标准化方法的意义,最终的最终还是要:拿来主义,能够在实践中使用. 动机:标准化的意义是什么? 我们为什么要标准化?想象我 ...
- C++入门-控制台版的通讯录管理系统
通讯录管理系统 1.系统需求 通讯录是一个可以记录亲人.好友信息的工具. 本教程主要利用C++来实现一个通讯录管理系统 系统中需要实现的功能如下: 添加联系人:向通讯录中添加新人,信息包括(姓名.性别 ...
- Linux监控CPU,内存,磁盘I/O
简单讲讲Linux下监控 [CPU] 监控CPU,top命令能够实时监控系统的运行状态,并且可以按照CPU.内存和执行时间进行排序,同时top命令还可以通过交互式命令进行设定显示,通过top命令可以查 ...
- 入门大数据---Hive的搭建
本博客主要介绍Hive和MySql的搭建: 学习视频一天就讲完了,我看完了自己搭建MySql遇到了一堆坑,然后花了快两天才解决完,终于把MySql搭建好了.然后又去搭建Hive,又遇到了很多坑,就这 ...