一、前言
不知道哪位古人说:人生三大境界。第一境界是:看山是山看水是水;第二境界是看山不是山看水不是水;第三境界:看山还是山看水还是水。
其实我想对于任何一门技术的学习都是这样。
形而上下者为之器,形而上者为之道。一直很喜欢自己大一的高数老师,老师是老教授了,他讲数学,会引申到建筑学,计算机科学,以及哲学再到生活中的常识。也能从其他学科、日常生活中,提取出数学的概念。我想,这就是形而上者了。
不胜望之
不多言,这里我们来深入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 <code>run</code> 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
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> 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
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> 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线程启动原理分析的更多相关文章

  1. Java 线程池原理分析

    1.简介 线程池可以简单看做是一组线程的集合,通过使用线程池,我们可以方便的复用线程,避免了频繁创建和销毁线程所带来的开销.在应用上,线程池可应用在后端相关服务中.比如 Web 服务器,数据库服务器等 ...

  2. java并发包&线程池原理分析&锁的深度化

          java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的, ...

  3. Java Reference核心原理分析

    本文转载自Java Reference核心原理分析 导语 带着问题,看源码针对性会更强一点.印象会更深刻.并且效果也会更好.所以我先卖个关子,提两个问题(没准下次跳槽时就被问到). 我们可以用Byte ...

  4. Spring5深度源码分析(三)之AnnotationConfigApplicationContext启动原理分析

    代码地址:https://github.com/showkawa/spring-annotation/tree/master/src/main/java/com/brian AnnotationCon ...

  5. 【Java线程与内存分析工具】VisualVM与MAT简明教程

    目录 前言 VisualVM 安装与配置 本地使用 远程监控 MAT 使用场景 安装与配置 获得堆转储文件 分析堆转储文件 窥探对象内存值 堆转储文件对比分析 总结 前言 本文将简要介绍Java线程与 ...

  6. JAVA线程池原理详解二

    Executor框架的两级调度模型 在HotSpot VM的模型中,JAVA线程被一对一映射为本地操作系统线程.JAVA线程启动时会创建一个本地操作系统线程,当JAVA线程终止时,对应的操作系统线程也 ...

  7. 【java】-- 线程池原理分析

    1.为什么要学习使用多线程? 多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担. 线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致 ...

  8. Java线程池原理及分析

    线程池是很常用的并发框架,几乎所有需要异步和并发处理任务的程序都可用到线程池. 使用线程池的好处如下: 降低资源消耗:可重复利用已创建的线程池,降低创建和销毁带来的消耗: 提高响应速度:任务到达时,可 ...

  9. Spring Boot 启动原理分析

    https://yq.aliyun.com/articles/6056 转 在spring boot里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启 ...

随机推荐

  1. HTML5:判断浏览器是否支持date类型

    在某些情况下,我们需要判断当前浏览器是否支持date类型,如果支持的话,可以让用户用原生的datepicker来选取日期.如果不支持,则我们需要用自己实现的datepicker类库,来提供给用户. 在 ...

  2. Linq:Linq实例1..More

    本文会不断更新应用实例. 需求1:对一个Rate列表的RateLevel属性求和,然后除以Rate列表的数量求平均值. 没有Linq的做法: Int rateLevel = ; foreach (Ra ...

  3. GPS-Graph Processing System 改动源代码经验总结 (四)

    HamaWhite原创,转载请注明出处.欢迎大家增加Giraph 技术交流群: 228591158 本文目的:在改动GPS源代码后,具体描写叙述怎样编译和分发到各Worker节点上. 以下以Graph ...

  4. Android --------- 自己定义VIew

    package com.example.coustomviewdemo; import android.R.interpolator; import android.content.Context; ...

  5. PyQt5学习随笔01--计算一个目录里我们码的代码行数&amp;&amp;PyQt的多线程通信

    今天突然想知道自学习Python以来我一共码了多少行代码了,于是写了一个简单的程序: __author__ = 'jiangzhiheng' # coding=utf-8 from PyQt5.QtC ...

  6. Java基础:String不可变性和final修饰

    转载请注明出处: jiq•钦's technical Blog - 季义钦 String的不可变性 Java规定String是不可变的(immutable).事实上这个不可变具备两层含义: 1 内容不 ...

  7. Ant报错之out of memory

    用Ant打包一个比較大的项目的时候,遇到OutOfMemory的问题,求助于Google和百度,网上的解决方式非常多,可是个人认为不够具体全面.我的问题须要综合两种方法才解决.把方案记下来.以期帮助大 ...

  8. 折腾开源WRT的AC无线路由之路-3

    来看看Netgear自带的用户界面和具体功能,看看它都能干什么. 開始 一开机.用网线连接到你的电脑上,在浏览器地址栏中输入Netgear默认的路由器地址192.168.1.1,第一次使用时它有个向导 ...

  9. React Native布局实践:开发京东client首页(三)——轮播图的实现

    上篇文章中,我们一起构建了京东client的TabBar.在本文中.将继续向大家介绍京东client首页轮播图及其下发功能button的开发方法,如今就让我们開始吧! 1.相关控件调研 眼下在Gith ...

  10. Java 二进制和十进制互转,二进制和BitSet互转

    /** * 二进制转十进制 * * @param binaryNumber * @return */ public static int binaryToDecimal(int binaryNumbe ...