线程基础知识_线程生命周期_从JVM内存结构看多线程下的共享资源

线程生命周期

在这里插入图片描述

线程状态

New: 线程创建(new Thread())
Runnable: 线程可运行(thread.start()), 注: 调用start并不一定是运行状态, 可能在等待CPU调度
Running: 线程进入运行状态
Blocked: 阻塞状态(object.wait, Thread.sleep)
Terminal: 死亡状态(线程正常/非正常结束运行)

thread.start()方法分析线程状态

start源码:

    public synchronized void start() {//使用if判断thread状态, threadStatus为0表示当前线程处于new状态if (threadStatus != 0)throw new IllegalThreadStateException();//将线程添加到对应线程组中group.add(this);boolean started = false;try {//native方法start0();started = true;} finally {try {//根据线程是否启动成功, 来决定是否添加threadStartFailed中if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {}}}

Thread使用threadStatus判断线程状态, 当线程启动后, 再次启动, 或者线程处于死亡状态, 还对线程进行start, 就会因为threadStatus抛出IllegalThreadStateException

Thread中的模板设计模式

模板设计模式: 接口定义算法骨架, 在不同的子类中, 对其算法骨架具有不同的实现方式
由于Thread实现了Runnable接口, 在Runnable中定义了算法骨架(run方法的定义), 因此创建线程一般都是重新run方法(继承Thread, 或者实现Runnable)
Thread中的run方法:

@Overridepublic void run() {//target代表new Thread(Runnable target)中传入的Runnable对象if (target != null) {target.run();}}

从上面的run方法得出, 创建线程的2中常用方式:

1. new Thread(){@Overridepublic void run() {//.........}};
2.new Thread(new Runnable() {@Overridepublic void run() {//.......}});

二者的区别:
方式1中, 不能做到run方法重用, 方式2中, 可以做到run方法重用(一个runnable对象能给多个thread使用)
这里一定要注意共享资源的使用, 这里很容易让人误解: 多个线程共享一个资源的时候, 必须保证这样的资源只有一份, 解决方式有: 当多个runnable对象时, 多个thread, 可以设置共享变量是static类型, 当只存在一份runnbable对象, 可以设置共享变量是普通成员变量, 其实这两种方式都是类似的, 只是取决于使用哪种方式创建线程, 使用多个Runnable对象, 还是一个Runnable对象.

Thread的init

调用线程的构造方法的时候, 就会自动调用Thread的init方法

	public Thread() {init(null, null, "Thread-" + nextThreadNum(), 0);}//线程初始化操作//stackSize: 设置线程内递归深度private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {//此时获取的线程是main线程, main线程执行子线程的初始化操作, 只有当子线程//执行run操作的时候, 才意味着子线程启动Thread parent = currentThread();SecurityManager security = System.getSecurityManager();//设置当当前线程的groupif (g == null) {if (security != null) {g = security.getThreadGroup();}if (g == null) {g = parent.getThreadGroup();}}g.checkAccess();if (security != null) {if (isCCLOverridden(getClass())) {security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);}}g.addUnstarted();//根据父线程属性, 设置当前线程的状态this.group = g;this.daemon = parent.isDaemon();this.priority = parent.getPriority();if (security == null || isCCLOverridden(parent.getClass()))this.contextClassLoader = parent.getContextClassLoader();elsethis.contextClassLoader = parent.contextClassLoader;this.inheritedAccessControlContext =acc != null ? acc : AccessController.getContext();this.target = target;setPriority(priority);if (inheritThreadLocals && parent.inheritableThreadLocals != null)this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);this.stackSize = stackSize;tid = nextThreadID();}

从init可以看出
当线程创建的时候, 如果不指定group, priority等属性的时候, 默认使用父线程(例如: main线程创建一个新的线程)的值
一个线程的创建, 依赖与另外一个线程(注: main线程的创建依赖于JVM)
当创建线程的时候, 没有指定线程的Group, 默认使用父线程所在的Group

ThreadGroup测试

思考引发: 能否使用ThreadGroup进行线程通信

/*** 测试ThreadGroup* @author regotto*/
public class ThreadGroupTest1 {public static void main(String[] args) {ThreadGroup group = new ThreadGroup("g1");System.out.println(group.getParent().getName());new Thread(group, "t1"){@Overridepublic void run(){//当前线程组可以对父类线程组或父类线程组中线程进行只读操作, 写操作将会出现异常System.out.println(Thread.currentThread().getThreadGroup().getParent().getName());}}.start();try {Thread.sleep(2_000);} catch (InterruptedException e) {e.printStackTrace();}//当线程组内线程(不是守护线程)都没有存活, 线程组不会destroy, 需要主动destroySystem.out.println("group.isDestroyed()" + group.isDestroyed());group.destroy();System.out.println("group.isDestroyed()" + group.isDestroyed());//将中断group中所有线程
//        group.interrupt();//将group中线程copy到线程数组中
//        group.enumerate(new Thread[10]);}
}

结论:
可以使用ThreadGroup进行线程通信, 但是这种通信存在局限性, 可以使用ChildThreadGroup获取ParentThreadGroup信息, 但是反过来就不行, 并且Child只能读取Parent的信息, 不能进行写操作

守护线程

用通俗的话来描述, 守护线程就是后台线程, 处理后台任务, 当前台任务执行完, 守护线程自动销毁, 例如JVM就是守护线程
守护线程一般用于垃圾清理, 监控

/*** 测试Daemon线程* 结论: 只有当JVM中所有线程结束,Daemon才会结束, 即使线程组不同* @author regotto*/
public class DaemonThreadTest {public static void main(String[] args) {Thread T = new Thread(new ThreadGroup("group1"), () -> {Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getThreadGroup() + "心跳线程开始");while (Thread.currentThread().isAlive()) {System.out.println(Thread.currentThread().getThreadGroup() + "心跳线程存活");}});t1.setDaemon(true);t1.start();System.out.println(Thread.currentThread().getThreadGroup() + "I/O操作线程");try {TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getThreadGroup() + "I/O线程结束");}).start();try {TimeUnit.MILLISECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getThreadGroup() + "main thread dead");}
}

T线程启动, 启动一个守护线程t1开始心跳, 当T线程死亡, 守护线程自动退出心跳结束

ThreadHook

ThreadHook: 线程钩子, 功能类似于守护线程(守护线程在前台线程运行中, 也都一直运行, 但是线程钩子只有最后程序结束,时 才执行thread.start操作), 当程序退出时, 负责垃圾清理或执行最后处理工作
ThreadHook测试

/*** Hook在接收到程序退出信号的时候才会退出, 当直接kill进程且等级为10, Hook不会执行* Hook可以负责资源释放操作(一定不要在Hook中执行很耗时的操作, 会导致程序不能退出)* @author regotto*/
public class ThreadHook {public static void main(String[] args) {//Hook可以注册多个, 在JVM进程将要退出, 执行Hook中线程(多个Hook, 则并行执行)Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("Hook1 running")));Runtime.getRuntime().addShutdownHook(new Thread(){@Overridepublic void run() {System.out.println("Hook2 running");}});   }
}

从JVM内存结构看多线程下的共享资源

在这里插入图片描述

各个模块的简单描述

1.方法区: 存放.class文件, 类信息, 常量, 静态变量
2.程序计数器: 当.class文件加载JVM的方法区中时, 当前线程利用程序计数器执行方法区中的字节码指令由于不同的线程执行指令不能相互影响, 因此设置程序计数器为线程私有
3.Java虚拟机栈: Java虚拟机栈是一个栈结构, 存放栈帧, 每一个栈帧就是一次方法调用(栈帧中存放方法局部变量表, 操作栈, 动态链接, 方法出口等), 不同线程执行过程调用的方法顺序不同, 因此Java虚拟机栈就是线程私有的每一个线程的Java虚拟机栈生命期与线程相同
4.本地方法栈: 与Java虚拟机栈类似, 只是本地方法栈存放线程调用native方法的栈帧情况
5.堆内存: 存放公共资源, JVM运行过程中存放所有生成的对象, 被所有线程共享
7.直接内存: 此块区域归属于native方法(C/C++)执行时用的堆空间

注: 由于Java虚拟机栈属于线程私有, 当每个线程的Java虚拟机空间越大, 那么能创建的线程就越少

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468580.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CPU,内核,寄存器,缓存,RAM,ROM的知识

偷偷说一下,这个图片是晚上 8 点钟的太阳,一个骚男华为总工发给我的,有时间还是想出去走走很多人使用计算机,但是对计算机却不是很懂,即使是作为非常资深的码农,也不能做到心中有剑的地步,那么怎…

mft按钮设计_哈汽机组660MW超临界空冷机组ETS设计及逻辑说明

点击上方“火力发电集控运行”关注公众号,加微信号:jikonglingmi,备注:集控运行,拉你入集控运行技术交流群,共同学习、共同进步。更多学习题库,请进入首页菜单选择一、 基本设计思想1、既防拒动…

JVM运行参数_JVM内存模型_常用内存分析工具

JVM运行参数 常见标准参数 -showversion: 显示当前JVM版本等信息-D设置系统属性参数: /*** 测试* author regotto*/ public class JvmTest {public static void main(String[] args) {//获取系统参数String str System.getProperty("str");System.out.println(st…

2019 年入门AI算法工程师,你需要掌握什么技能?

一篇推文,感兴趣的同学可以了解一下,有需要请继续往下阅读,没有需要的打扰各位金主了-----人工智能与自然语言处理/计算机视觉课培训招生Artificial Intelligence ForNLP/CV Courses真实企业级项目驱动找工作拿不到offer全额退款GitHub年度活…

Java常见GC算法_垃圾收集器及内存分配_G1垃圾收集器

常见GC算法 引用计数法: 每个对象都有一个计数器, 对象被引用一次, 计数器1, 当对象引用失败一次. 计数器-1, 当对象计数器等于0, 说明对象没有被应用, 就可GC 优: 运行过程中, 可随时检查对象计数器, 进行GC, 且GC过程, 应用无需暂停, 执行速度快(单个对象GC不会影响其他对…

是应该有点兴奋剂刺激下

不知道你有没有发现一个问题,发现身边的很多人工资很高,但是好像总还是缺点什么,总是对生活提不起兴趣,似乎对任何事情都失去热情,每天拿着手机,点来点去,每天都在抱怨,每天都好像在…

你应该知道为什么需要内核

不知道大家想过这个问题没有?为什么要有内核?如果没有内核有怎么样呢?因为有了 unix 内核,有了 minix 后面又有了 Linux 内核,内核这个思想越来越根深蒂固,估计没学什么计算机知识的人都能侃出点所以然来。…

2019深圳入户攻略

我上次说了,等我把户口办好了,我就把攻略写出来,给大家一个参考,今天刚好是我最后的一个步骤,去派出所把身份证给办了,好了,我以后就是深圳人了,来深圳快十年了,现在才真…

NIO之ByteBuffer_NIO之网络IO_与ChannelNetty初窥门径

NIO之ByteBuffer与Channel 传统IO:byte[] < inputStream < 文件 > outputStream > byte[] NIO:文件 > inputChannel <> buffer <> outputChannel > 文件文件 < inputChannel <> outputChannel > 文件文件复制, 并测试ByteBuffer常用…

mp4 拍摄时间如何看_时间不多了,如何备考期末最有效?这些复习技巧,看了你就会了...

再过不到一个月就要过年了&#xff0c;虽然天气越来越冷&#xff0c;但是有阻挡不了年度大戏“期末考”的前进步伐&#xff0c;尤其是对于紧张复习之中的高考备考生而言&#xff0c;高三第一学期的期末考就可以算是对自己第一轮复习的一个检验&#xff0c;如果成绩不理想&#…

vue实战学习第二天

1.怎么运行别人的项目 步骤一&#xff1a;搭建脚手架&#xff1a;npm i -g vue-cli 步骤二&#xff1a;vue init webpack 不要一直默认回车&#xff0c;去除一些不必要的依赖&#xff0c;减少代码的编写难度 步骤三&#xff1a;下载依赖的文件 npm i &#xff08;可能有些人会…

Netty之自定义RPC

需求分析 使用netty实现方法远程调用, 在client调用本地接口中方法时, 使用反射进行远程调用, server执行完结果后, 将执行结果进行封装, 发送到client RPC调用模型: 1. 服务消费方(client)以本地调用方式调用服务 2. client stub 接收到调用后负责将方法、参数等封装成能够…

samba 服务器搭建

为什么要搭建samba 服务器我在 windows 下安装了个虚拟机&#xff0c;然后想两边同步下资料&#xff0c;原来虚拟机是可以共享文件的&#xff0c;可是不知道什么见鬼了&#xff0c;就是不行&#xff0c;没办法了&#xff0c;我只好拿出我的杀手锏&#xff0c;安装 samba。这个在…

一直想说的,技术职业化

最近后台有人一直跟我说&#xff0c;为什么不好好写一篇技术比较强的文章&#xff0c;说实话&#xff0c;最近时间比较紧张&#xff0c;早上 8 点出门&#xff0c;晚上12点左右到家。刚好今天整理了一个不错文章的列表&#xff0c;明天发出来&#xff0c;希望给学习的同学们有点…

MyBatis初级入门及常见问题

入门案例 创建maven工程 项目目录结构: 首先在maven的pom.xml导入Mybatis和MySQL的依赖坐标: <dependencies><!--Junit测试依赖--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</…

读书笔记:Information Architecture for the World Wide Web, 3rd Edition 北极熊 第一部分 1-3...

Introducing Information Architecture 信息架构简介 Chapter 1 Defining Information Architecture 信息架构的意义&#xff08;我们盖房子&#xff0c;之后&#xff0c;房子影响我们&#xff09; A DefinitionTablets, Scrolls, Books, and Libraries 石板、卷轴、书籍&#…

Mybatis执行流程分析_自定义简易Mybatis框架

自定义简易Mybatis框架 Mybatis执行流程分析 Mybatis代码编写流程: Mybatis配置文件加载过程: 需求分析及技术概述 根据上述的功能结构图, 得出如下需求: 1. 需要具有配置文件加载模块. 2. 支持使用构建者进行SessionFactory构建. 3. 支持使用工厂模式构建Session对象. 4.…

给大家推荐一个优质Linux内核技术公众号-Linux阅码场

作为一个Linux 技术公众号的作者&#xff0c;我觉得有义务推荐优秀的公众号&#xff0c;推广内容&#xff0c;希望对大家的学习有所帮助~Linux阅码场是一个专注Linux内核和系统编程与调试调优技术的公众号&#xff0c;它的文章云集了国内众多知名企业一线工程师的心得。无论你工…

图数据库_ONgDB图数据库与Spark的集成

快速探索图数据与图计算图计算是研究客观世界当中的任何事物和事物之间的关系&#xff0c;对其进行完整的刻划、计算和分析的一门技术。图计算依赖底于底层图数据模型&#xff0c;在图数据模型基础上计算分析Spark是一个非常流行且成熟稳定的计算引擎。下面文章从ONgDB与Spark的…

2019 高考填报志愿建议

2019 高考填报志愿建议1、城市很关键&#xff0c;在大城市上学和小地方上学会有很大的不同&#xff0c;现在很多毕业生毕业后会往北上广深跑&#xff0c;很多原因是因为这里的就业机会多&#xff0c;薪资比内地好太多了&#xff0c;如果你大学就能在这样的地方上学&#xff0c;…