• 为什么启动线程是start方法?


    为什么启动线程是start方法

     

    十年可见春去秋来,百年可证生老病死,千年可叹王朝更替,万年可见斗转星移。

                 凡人如果用一天的视野,去窥探百万年的天地,是否就如同井底之蛙?

     

    背景:启动线程是start() 还是run() 方法?相信这个问题很多人都知道是start(),但是如果我再问下去呢,为什么是start()?你会如何作答呢?

     

    一、理论课

    当用start()开始一个线程后,线程就进入就绪状态,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行;但是这并不意味着线程就会立即运行,只有当cpu分配时间片时,这个线程获得时间片时,才开始执行run()方法;start()方法去调用run(),而run()方法则是需要去重写的,其包含的是线程的主体(真正的逻辑)。

    二、线程六大状态

    Java 中,定义了 6 种线程状态,NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITING和TERMINATED

    1 public enum State{
    2    NEW,  
    3    RUNNABLE,
    4    BLOCKED,
    5    WAITING,
    6    TIMED_WAITING,
    7    TERMINATED;     
    8 }

    6 种线程状态关系图

     

    三、代码层次

    杨总说的,一切问题归咎于源码!

    start 方法的源码:

    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.
             *
             * A zero status value corresponds to state "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 {
                /**
                 * start0() 是启动多线程的关键
                 * 执行完成之后,新的线程已经在运行了
                 * 这里会创建一个新的线程,是一个 native 方法
                 */
                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 */
                }
            }
        }
    

     run方法源码:

     @Override
        public void run() {
         // 简单的运行,不会新起线程,target 是 Runnable
            if (target != null) {
                target.run();
            }
        }
    

    从start()和run()方法的源码可以看出,start方法的源码也没几行代码,最主要的是 start0() 方法;run() 方法的源码也比较简单的,就是一个普通方法的调用;重点是这个 start0() 方法,她是真正实现多线程的关键。

    start0方法源码:

    // native :就是本地方法 
    private native void start0(); 
    

    关于native方法简述:

    1、被native关键字修饰的方法叫做本地方法,本地方法和其它方法不一样,本地方法意味着和平台有关,因此使用了native的程序可移植性都不太高;

    2、native方法在JVM中运行时数据区也和其它方法不一样,它有专门的本地方法栈;

    3、native方法主要用于加载文件和动态链接库,由于Java语言无法访问操作系统底层信息(比如:底层硬件设备等),这时候就需要借助C语言来完成了;

    4、被native修饰的方法可以被C语言重写。

    Java 跨平台图

     

    在start()中start0 被标记成 native ,也就是本地方法,并不需要我们去实现或者了解,只要了解下为什么 start0() 会标记成 native ?

    如上图,start() 方法调用 start0() 方法后,该线程并不一定会立马执行,只是将线程变成了可运行状态(NEW ---> RUNNABLE);具体什么时候执行,取决于 CPU ,由 CPU 统一调度;我们又知道 Java 是跨平台的,可以在不同系统上运行,每个系统的 CPU 调度算法不一样,所以就需要做不同的处理,这件事情就只能交给 JVM 来实现了,start0() 方法自然就表标记成了 native。

     四、总结

    Java 中实现真正的多线程是 start 中的 start0() 方法,run() 方法只是一个包含业务逻辑普通的方法;start是启动多线程的唯一方式,其使得线程由创建态到就绪态,而这个线程是否被运行是由系统调度所决定的。

     

      十年可见春去秋来

        百年可证生老病死

          千年可叹王朝更替

            万年可见斗转星移

              凡人如果用一天的视野

                   去窥探百万年的天地

                      是否就如同井底之蛙?

     

  • 相关阅读:
    考研系列一-线性表类(顺序存储)
    因特网协议分层及它们的服务模型
    矩阵归零
    字符编码(续)---Unicode与ANSI字符串转换以及分辨字符编码形式
    奇妙的位运算
    一道面试题Lintcode196-Find the Missing Number
    错误处理
    px 和 em 的区别
    简述同步和异步的区别
    简述一下 src 与 href 的区别
  • 原文地址:https://www.cnblogs.com/taojietaoge/p/13154638.html
Copyright © 2020-2023  润新知