java看山不是山_java线程启动原理分析
一、前言
不知道哪位古人说:人生三大境界。第一境界是:看山是山看水是水;第二境界是看山不是山看水不是水;第三境界:看山还是山看水还是水。
其实我想对于任何一门技术的学习都是这样。
形而上下者为之器,形而上者为之道。一直很喜欢自己大一的高数老师,老师是老教授了,他讲数学,会引申到建筑学,计算机科学,以及哲学再到生活中的常识。也能从其他学科、日常生活中,提取出数学的概念。我想,这就是形而上者了。
不胜望之
不多言,这里我们来深入java底层,看下java表皮之下的筋肉以及内脏。
二、从一段代码展开
package thread;
/**
* @author xuyuanpeng
* @version 1.0
* @date 2019-05-17 17:04
*/
public class ThreadMain {
public static void main(String[] args) {
Thread thread=new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2=new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
log("线程1开始");
thread.start();
log("线程1结束");
log("线程2开始");
t2.run();
log("线程2结束");
}
public static void log(String msg){
System.err.print(System.currentTimeMillis());
System.out.println(">>>"+msg);
}
public static void log(){
log("");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
这里可以思考下输出的结果:
1
2
3
铛铛铛档
Connected to the target VM, address: ‘127.0.0.1:51304’, transport: ‘socket’
1558085396255>>>线程1开始
1558085396255>>>线程1结束
1558085396255>>>线程2开始
1558085397255>>>线程2结束
Disconnected from the target VM, address: ‘127.0.0.1:51304’, transport: ‘socket’
细心的同学肯定已经发现了
线程1是start的方式启动,而线程2是run方法启动
差异在哪?
线程1执行start,并没有阻塞线程
而线程2的run方法,阻塞了线程。何改咯?┓( ´∀` )┏
为什么是这样的呢?start与run的区别究竟在哪呢?让我们深入她,张爱玲说,了解一个女人最好的通道就是XX,所以让我们深入她,再了解她。
三、JDK源码分析
1、start方法
public synchronized void start() {
/**
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the run
method of this thread.
*
* 1、start方法将导致当前线程开始执行。由JVM调用当前线程的run方法。
*
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* start
method) and the other thread (which executes its
* run
method).
*
* 2、结果是 调用start方法的当前线程 和 执行run方法的另一个线程 同时运行。
*
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* 3、多次启动线程永远不合法。 特别是,线程一旦完成执行就不会重新启动。
*
* @exception IllegalThreadStateException if the thread was already started.
* 如果线程已启动,则抛出异常。
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* 4、对于由VM创建/设置的main方法线程或“system”组线程,不会调用此方法。
* 未来添加到此方法的任何新功能可能也必须添加到VM中。
*
* A zero status value corresponds to state "NEW".
* 5、status=0 代表是 status 是 "NEW"。
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented.
*
* 6、通知组该线程即将启动,以便将其添加到线程组的列表中,
* 并且减少线程组的未启动线程数递减。
*
* */
group.add(this);
boolean started = false;
try {
//7、调用native方法,底层开启异步线程,并调用run方法。
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then it will be passed up the call stack
* 8、忽略异常。 如果start0抛出一个Throwable,它将被传递给调用堆栈。
*/
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
start方法用synchronized修饰,为同步方法;
虽然为同步方法,但不能避免多次调用问题,用threadStatus来记录线程状态,如果线程被多次start会抛出异常;threadStatus的状态由JVM控制。
使用Runnable时,主线程无法捕获子线程中的异常状态。线程的异常,应在线程内部解决。
2、native start0方法
private native void start0();
1
native 是声明本地方法,在此处是JVM中的方法。
3、run方法
/**
* If this thread was constructed using a separate
* Runnable
run object, then that
* Runnable
object's run
method is called;
* otherwise, this method does nothing and returns.
*
* Subclasses of Thread
should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
run方法就很简单了,就是回调了Runable的run()接口
导致Thread写的@Overwrite void run() 方法直接是在主线程执行,导致阻塞了主线程。
四、总结
到此我们就知道了,start会使重写的run方法被虚拟机调用,是在子线程中执行的run方法
而直接调用线程的run方法,他是内部回调了run接口,导致直接执行了Runable.run的重写内容。相当于直接在主线程中执行。
---------------------
java看山不是山_java线程启动原理分析相关推荐
- java并发包线程池原理分析锁的深度化
java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素 ...
- python内嵌函数和闭包与java 匿名内部类_Java匿名内部类构造原理分析
很明显,我们的匿名内部类有了名字 InnerTest$1,而且是继承自 Test class InnerTest$1 extends Test 这个类中有一个成员final InnerTest thi ...
- java线程池饱和策略_干货:Java几种线程池的分析和使用。
原标题:干货:Java几种线程池的分析和使用. 利用线程池的优势: 1.降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 2.提高响应速度.当任务到达时,任务可以不需要等到线程创建 ...
- Tomcat线程池监控及线程池原理分析
目录 一.背景 二.tomcat线程池监控 三.tomcat线程池原理 四.总结 一.背景 我们都知道稳定性.高可用对于一个系统来讲 ...
- 4、Eureka 源码解析 之 Eureka Client 启动原理分析
在前面的一篇文章 3.Eureka 源码解析 之 Eureka Server 启动原理分析当中我们分析了一下 Eureka Server 的启动.在集群环境下 Eureka Server 相互之前需要 ...
- Spring Boot(四):Spring Boot启动原理分析
文章目录 Spring Boot启动原理分析 一.依赖导入原理 二.Spring Boot包扫描原理 三.Spring Boot自动配置原理 Spring Boot启动原理分析 一.依赖导入原理 父项 ...
- java 线程池原理分析
一.为什么使用线程池 1.降低资源消耗,减少线程创建和销毁次数,每个工作线程可以重复利用,执行多个任务 2.可根据系统承受能力,调整工作线程的数目,防止消耗过多的内存 二.java 线程池使用 Exe ...
- java 线程池的理解_JAVA线程池原理的理解
线程池原理基础理解: 线程池初始化规定个数的线程,然后这些线程一直运行,并且监控线程队列,只要线程队列被添加进线程,那么线程池不断从队列中取出线程运行.直到队列中的线程为空.实例代码如下: packa ...
- Java-Java中的线程池原理分析及使用
文章目录 概述 线程池的优点 线程池的实现原理 线程池的使用 创建线程池 向线程池中提交任务 关闭线程池 合理的配置线程池 线程池的监控 概述 我们在上篇博文 Java-多线程框架Executor解读 ...
最新文章
- 03 Java程序员面试宝典视频课程之常用类
- 探寻背后的机制化繁为简:网站程序升级不过是文件同步
- 踩到一个Emit的坑,留个纪念
- 怎么样让自己更加从容的面对生活
- delphi7注册码
- Numpy练习题100题-提高你的数据分析技能
- linux命令crontab
- springboot整合elasticsearch_Spring Boot学习10_整合Elasticsearch
- python cpu_python 让cpu满载
- 疑似一加7 Pro 5G版通过3C认证:配备30W快充头
- Eclipse启动时报错Java was started but returned exit code=13
- 基于LSTM搭建文本情感分类的深度学习模型:准确率95%
- 顶尖的语音识别软件――Nuance Recognizer_语音识别_CTI论坛
- 大家社区荣获最具影响力品牌
- R与Python手牵手:数据科学导论系列(包的载入)
- struts2的package和result的标签的属性
- RNAcentral:非编码RNA数据库
- linux思源黑体乱码,deepin终端使用思源黑体做中文字体
- 8种经典的统计学悖论18种经典的哲学悖论
- 解密DB2独步江湖之五大法宝