Java中线程的run()方法和start()方法有什么区别?

    欢迎大家关注我的公众号,有问题可以及时和我交流。 image.png

    由于Java是支持单继承的(接口除外),所以我们普遍启动线程的方式都是实现Runnable接口并重写run()方法。先来看下面一个简单的实例:

1public class MyRunnable implements Runnable { 2 @Override 3 public void run() { 4 try { 5 // 睡眠3秒 6 TimeUnit.SECONDS.sleep(3); 7 } catch (InterruptedException e) { 8 e.printStackTrace(); 9 } 10 System.out.println("实现Runnable接口的线程"); 11 } 12} 13 14 15public class MyRunnableTest { 16 public static void main(String[] args) throws InterruptedException { 17 System.out.println("--------开始调用线程--------"); 18 Thread thread= new Thread(new MyRunnable()); 19 thread.run(); 20 System.out.println("--------调用线程结束--------"); 21 } 22}

    你能猜出上面执行的顺序吗?或许你可以试着尝试用机器运行一下,把得到的结果和你的猜想进行一个验证。

    运行之前请仔细思考一下,如果run()方法启动了一个线程的话,那么在“MyRunnable”中我们设置了休眠,主线程应该继续往下执行,那么输出的结果应该就是:

1--------开始调用线程-------- 2--------调用线程结束-------- 3实现Runnable接口的线程

    但是,我们来看下实际的运行结果:

1--------开始调用线程-------- 2实现Runnable接口的线程 3--------调用线程结束--------

    很显然run()方法其实是按照顺序执行的,并没有真正的启动一个线程。在解释原因之前我们先来看看下面这段代码:

Thread thread= new Thread(new MyRunnable());

    这段代码也就是构造了一个thread对象,并且把我们实现的Runnable传递了进去。我们点进源码查看一下这个构造方法。

1public Thread(Runnable target) { 2 init(null, target, "Thread-" + nextThreadNum(), 0); 3}

    在这里,先不需要了解构造方法的每一参数,只需要注意一下我们传递进来的“MyRunnable”它是一个Runnable对象,这里赋给了"target"。请先记住这一点,他在后面的解析中很重要。 重点来了:接下来我们再来看看所谓的线程启动的代码:

thread.run();

    我们还是点击进去源码,能看到如下的代码示例:

1@Override 2public void run() { 3 if (target != null) { 4 target.run(); 5 } 6}

    我想看到“@Override”这个关键词,大家应该立刻想到他是一个方法的重写,然后我们发现,其实Thread类也是实现了“Runnable”接口的。这里看到“target ”其实就是我们自定义的MyRunnable,“target.run()”最终调用的方法也就是“MyRunnable.run()”。因此可以得出一个结论,run()方法并不会真正的启动一个线程,调用run()方法,就相当于调用了一个普通方法,程序还是按照顺序执行的。下图展示了调用的过程。 image.png     然后我们尝试正确的启动一个线程,代码如下:

1public class MyRunnableTest { 2 public static void main(String[] args) throws InterruptedException { 3 System.out.println("--------开始调用线程--------"); 4 Thread thread= new Thread(new MyRunnable()); 5 thread.start(); 6 System.out.println("--------调用线程结束--------"); 7 } 8 9}

    运行结果:

1--------开始调用线程-------- 2--------调用线程结束-------- 3实现Runnable接口的线程

    通过运行程序可以发现,这次的输入内容是符合我们之前的预期的。我们再次点进“start()”的源码进行查看(不用每一行都了解其原理,先大致了解即可。):

1public synchronized void start() { 2 if (threadStatus != 0) throw new IllegalThreadStateException(); 3 group.add(this); 4 boolean started = false; 5 try { 6 start0(); 7 started = true; 8 } finally { 9 try { 10 if (!started) { 11 group.threadStartFailed(this); 12 } 13 } catch (Throwable ignore) { 14 } 15 }

    这里比较重要的一个方法就是“start0()”跟进“start0()”的源码能够发现这个方法调用的是一个本地的方法,也就是交给底层去实现了。

private native void start0();

image.png

Thread.start()--->start0()--->run()

**    总的来说我们启动一个线程的话,构造好线程之后,调用“start()”方法才算真正的启动了一个线程。**     欢迎大家关注我的公众号,有问题可以及时和我交流。 image.png

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Java日期时间API系列31

  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前

Java面试系列

实现多线程的方式继承Thread类,重写run方法,调用start方法启动线程实现Runnable接口,重写run方法,调用start方法启动线程实现Callable接口,重写call方法,并用FutureTask包装,在newThread中传入FutureTask,然后调用start方

KVM调整cpu和内存

一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid