欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java线程启动原理分析

程序员文章站 2022-10-08 18:55:22
一、前言 不知道哪位古人说:人生三大境界。第一境界是:看山是山看水是水;第二境界是看山不是山看水不是水;第三境界:看山还是山看水还是水。 其实我想对于任何一门技术的学习都是这样。 形而上下者为之器,形而上者为之道。一直很喜欢自己大一的高数老师,老师是老教授了,他讲数学,会引申到建筑学,计算机科学,以 ......

一、前言

不知道哪位古人说:人生三大境界。第一境界是:看山是山看水是水;第二境界是看山不是山看水不是水;第三境界:看山还是山看水还是水。
其实我想对于任何一门技术的学习都是这样。
形而上下者为之器,形而上者为之道。一直很喜欢自己大一的高数老师,老师是老教授了,他讲数学,会引申到建筑学,计算机科学,以及哲学再到生活中的常识。也能从其他学科、日常生活中,提取出数学的概念。我想,这就是形而上者了。
不胜望之
不多言,这里我们来深入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
铛铛铛档

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,它将被传递给调用堆栈。
                 */
            }
        }
    }

start方法用synchronized修饰,为同步方法;
虽然为同步方法,但不能避免多次调用问题,用threadstatus来记录线程状态,如果线程被多次start会抛出异常;threadstatus的状态由jvm控制。
使用runnable时,主线程无法捕获子线程中的异常状态。线程的异常,应在线程内部解决。

2、native start0方法

private native void start0();

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

run方法就很简单了,就是回调了runable的run()接口
导致thread写的@overwrite void run() 方法直接是在主线程执行,导致阻塞了主线程。

四、总结

到此我们就知道了,start会使重写的run方法被虚拟机调用,是在子线程中执行的run方法
而直接调用线程的run方法,他是内部回调了run接口,导致直接执行了runable.run的重写内容。相当于直接在主线程中执行。

五、参考

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