Java中为什么start方法不能重复调用而run方法可以?

 更新时间:2022年05月09日 11:39:56   作者:​ Java中文社群   ​  
这篇文章主要介绍了Java中为什么start方法不能重复调用而run方法可以?带着疑问一起学习下面文章的详细内容吧

初学线程时,总是将 run 方法和 start 方法搞混,虽然二者是完全不同的两个方法,但刚开始使用时很难分清,原因就是因为初次使用时效果貌似是一样的,

如下代码所示:

public static void main(String[] args) {
    // 创建线程一
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("执行线程一");
        }
    });
    // 调用 run 方法
    thread.run();

    // 创建线程二
    Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("执行线程二");
        }
    });
    // 调用 start 方法
    thread2.start();
}

以上程序的执行结果如下: 

 从上述结果可以看出,二者调用之后的执行效果都是一样,都可以成功执行任务。但是,如果在执行线程的时候,加上打印当前线程的名称就能看出二者的不同了,

如下代码所示:

public static void main(String[] args) {
    // 创建线程一
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            // 获取到当前执行线程
            Thread currThread = Thread.currentThread();
            System.out.println("执行线程一,线程名:" + currThread.getName());
        }
    });
    // 调用 run 方法
    thread.run();

    // 创建线程二
    Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            // 获取到当前执行线程
            Thread currThread = Thread.currentThread();
            System.out.println("执行线程二,线程名:" + currThread.getName());
        }
    });
    // 调用 start 方法
    thread2.start();
}

以上程序的执行结果如下: 

 从上述结果我们可以看出:当调用 run 方法时,其实是调用当前主程序 main 来执行方法体的;而调用 start 方法才是真正的创建一个新线程来执行任务。

区别1

run 方法和 start 方法的第一个区别是:调用 start 方法是真正开启一个线程来执行任务,而调用 run 方法相当于执行普通方法 run,并不会开启新线程,

如下图所示: 

区别2

run 方法和 start 方法的第二个区别是:run 方法也叫做线程体,它里面包含了具体要执行的业务代码,当调用 run 方法时,会立即执行 run 方法中的代码(如果当前线程时间片未用完);而调用 start 方法时,是启动一个线程并将线程的状态设置为就绪状态。也就是说调用 start 方法,并不会立即执行。

区别3

因为 run 方法是普通方法,而普通方法是可以被多次调用的,所以 run 方法可以被调用多次;而 start 方法是创建新线程来执行任务,因为线程只能被创建一次,所以它们的第三个区别是:run 方法可以被调用多次,而 start 方法只能被调用一次。

 测试代码如下:

// 创建线程一
Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        // 获取到当前执行的线程
        Thread currThread = Thread.currentThread();
        System.out.println("执行线程一,线程名:" + currThread.getName());
    }
});
// 调用 run 方法
thread.run();
// 多次调用 run 方法
thread.run();

// 创建线程二
Thread thread2 = new Thread(new Runnable() {
    @Override
    public void run() {
        // 获取到当前执行的线程
        Thread currThread = Thread.currentThread();
        System.out.println("执行线程二,线程名:" + currThread.getName());
    }
});
// 调用 start 方法
thread2.start();
// 多次调用 start 方法
thread2.start();

以上程序的执行结果如下: 

 从上述结果可以看出,run 方法多次调用可用正常执行,而第二次调用 start 方法时程序就报错了,提示“IllegalThreadStateException”非法线程状态异常。

为什么start不能被重复调用?

要找到这个问题的答案,就要查看 start 方法的实现源码,它的源码如下: 

 从 start 源码实现的第一行,我们就可以得到问题的答案,因为 start 方法在执行时,会先判断当前线程的状态是不是等于 0,也就是是否为新建状态 NEW,如果不等于新建状态,那么就会抛出“IllegalThreadStateException”非法线程状态异常,这就是线程的 start 方法不能被重复调用的原因。 它的执行过程是:当线程调用了第一个 start 方法之后,线程的状态就会从新建状态 NEW,变为就绪状态 RUNNABLE,此时再次调用 start 方法,JVM 就会判断出当前的线程已经不等于新建状态,从而抛出 IllegalThreadStateException 非法线程状态异常。

总结

run 方法和 start 方法的主要区别如下:

  • 方法性质不同:run 是一个普通方法,而 start 是开启新线程的方法。
  • 执行速度不同:调用 run 方法会立即执行任务,调用 start 方法是将线程的状态改为就绪状态,不会立即执行。
  • 调用次数不同:run 方法可以被重复调用,而 start 方法只能被调用一次。

start 方法之所以不能被重复调用的原因是,线程的状态是不可逆的,Thread 在 start 的实现源码中做了判断,如果线程不是新建状态 NEW,则会抛出非法线程状态异常 IllegalThreadStateException。​

到此这篇关于Java中为什么start方法不能重复调用而run方法可以?的文章就介绍到这了,更多相关start与run方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java后缀数组之求sa数组的实例代码

    Java后缀数组之求sa数组的实例代码

    后缀数组就是一个字符串所有后缀大小排序后的一个集合,然后我们根据后缀数组的一些性质就可以实现各种需求。这篇文章主要介绍了Java后缀数组-求sa数组,需要的朋友可以参考下
    2018-04-04
  • MybatisPlus调用原生SQL的三种方法实例详解

    MybatisPlus调用原生SQL的三种方法实例详解

    这篇文章主要介绍了MybatisPlus调用原生SQL的三种方法,在有些情况下需要用到MybatisPlus查询原生SQL,MybatisPlus其实带有运行原生SQL的方法,我这里列举三种,需要的朋友可以参考下
    2022-09-09
  • 浅谈java字符串比较到底应该用==还是equals

    浅谈java字符串比较到底应该用==还是equals

    这篇文章主要介绍了浅谈java字符串比较到底应该用==还是equals,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • shiro之记住登录信息

    shiro之记住登录信息

    Shiro提供了记住我(RememberMe)的功能,当关闭浏览器时下次再次打开还能记住你的信息,下面小编给大家分享shiro之记住登录信息的相关知识,感兴趣的朋友一起看看吧
    2017-09-09
  • Spring中自动装配的4种方式

    Spring中自动装配的4种方式

    今天小编就为大家分享一篇关于Spring中自动装配的4种方式,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • Mybatis持久层框架入门之CRUD实例代码详解

    Mybatis持久层框架入门之CRUD实例代码详解

    这篇文章主要介绍了Mybatis持久层框架入门之CRUD实例,需要的朋友可以参考下
    2022-05-05
  • 完美解决gson将Integer默认转换成Double的问题

    完美解决gson将Integer默认转换成Double的问题

    下面小编就为大家带来一篇完美解决gson将Integer默认转换成Double的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • Java实现HashMap排序方法的示例详解

    Java实现HashMap排序方法的示例详解

    这篇文章主要通过一些示例为大家介绍了Java对HashMap进行排序的方法,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解一下
    2022-05-05
  • 详解时间轮TimeWheel的工作原理

    详解时间轮TimeWheel的工作原理

    时间轮(TimeWheel)作为一种高效率的计时器实现方案,在1987年发表的论文Hashed and Hierarchical Timing Wheels中被首次提出。本文主要来聊聊它的工作原理,感兴趣的可以了解一下
    2023-02-02
  • eclipse修改jvm参数调优方法(2种)

    eclipse修改jvm参数调优方法(2种)

    本篇文章主要介绍了eclipse修改jvm参数调优方法(2种),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-02-02

最新评论