java線程啓動原理分析

1、前言

不知道哪位古人說:人生三大境界。第一境界是:看山是山看水是水;第二境界是看山不是山看水不是水;第三境界:看山仍是山看水仍是水。
其實我想對於任何一門技術的學習都是這樣。
形而上下者爲之器,形而上者爲之道。一直很喜歡本身大一的高數老師,老師是老教授了,他講數學,會引伸到建築學,計算機科學,以及哲學再到生活中的常識。也能從其餘學科、平常生活中,提取出數學的概念。我想,這就是形而上者了。
不勝望之
很少言,這裏咱們來深刻java底層,看下java表皮之下的筋肉以及內臟。java

2、從一段代碼展開

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
鐺鐺鐺檔異步

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'socket

細心的同窗確定已經發現了
線程1是start的方式啓動,而線程2是run方法啓動
差別在哪?
線程1執行start,並無阻塞線程
而線程2的run方法,阻塞了線程。何改咯?┓( ´∀` )┏
爲何是這樣的呢?start與run的區別究竟在哪呢?讓咱們深刻她,張愛玲說,瞭解一個女人最好的通道就是XX,因此讓咱們深刻她,再瞭解她。ide

3、JDK源碼分析

一、start方法

public synchronized void start() {
        /**
      /**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</code> method of this thread.
     * 
     * 一、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).
     * 
     * 二、結果是 調用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.
     *
     * 三、屢次啓動線程永遠不合法。 特別是,線程一旦完成執行就不會從新啓動。
     * 
     * @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.
         * 
         * 四、對於由VM建立/設置的main方法線程或「system」組線程,不會調用此方法。 
         *    將來添加到此方法的任何新功能可能也必須添加到VM中。
         * 
         * A zero status value corresponds to state "NEW".
         * 五、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. 
         * 
         * 六、通知組該線程即將啓動,以便將其添加到線程組的列表中,
         *    而且減小線程組的未啓動線程數遞減。
         * 
         * */
        group.add(this);

        boolean started = false;
        try {
            //七、調用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 
                 * 八、忽略異常。 若是start0拋出一個Throwable,它將被傳遞給調用堆棧。
                 */
            }
        }
    }

start方法用synchronized修飾,爲同步方法;
雖然爲同步方法,但不能避免屢次調用問題,用threadStatus來記錄線程狀態,若是線程被屢次start會拋出異常;threadStatus的狀態由JVM控制。
使用Runnable時,主線程沒法捕獲子線程中的異常狀態。線程的異常,應在線程內部解決。源碼分析

二、native start0方法

private native void start0();

native 是聲明本地方法,在此處是JVM中的方法。學習

三、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();
        }
    }

run方法就很簡單了,就是回調了Runable的run()接口
致使Thread寫的@Overwrite void run() 方法直接是在主線程執行,致使阻塞了主線程。this

4、總結

到此咱們就知道了,start會使重寫的run方法被虛擬機調用,是在子線程中執行的run方法
而直接調用線程的run方法,他是內部回調了run接口,致使直接執行了Runable.run的重寫內容。至關於直接在主線程中執行。線程

5、參考

https://www.jianshu.com/p/8c16aeea7e1arest

相關文章
相關標籤/搜索