zoukankan      html  css  js  c++  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 <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的重写内容。相当于直接在主线程中执行。
    --------------------- 

  • 相关阅读:
    高精度减法
    HDU 4460 Friend Chains
    POJ 2386 Lake Counting
    POJ 1852 Ants
    HDU 1240 Asteroids!
    SQL注入之Sqli-labs系列第三十六关(基于宽字符逃逸GET注入)和三十七关(基于宽字节逃逸的POST注入)
    SQL注入之Sqli-labs系列第三十四关(基于宽字符逃逸POST注入)和三十五关
    SQL注入之Sqli-labs系列第三十三关(基于宽字符逃逸注入)
    SQL注入之Sqli-labs系列第三十关(基于WAF防护的双引号报错注入)和三十一关
    墨者-uWSGI 漏洞复现(CVE-2018-7490)
  • 原文地址:https://www.cnblogs.com/ly570/p/10942349.html
Copyright © 2011-2022 走看看