Java中启动线程start和run的两种方法

一、区别

Java中启动线程有两种方法,继承Thread类和实现Runnable接口,由于Java无法实现多重继承,所以一般通过实现Runnable接口来创建线程。但是无论哪种方法都可以通过start()和run()方法来启动线程,下面就来介绍一下他们的区别。

start方法:

通过该方法启动线程的同时也创建了一个线程,真正实现了多线程。无需等待run()方法中的代码执行完毕,就可以接着执行下面的代码。此时start()的这个线程处于就绪状态,当得到CPU的时间片后就会执行其中的run()方法。这个run()方法包含了要执行的这个线程的内容,run()方法运行结束,此线程也就终止了。

run方法:

通过run方法启动线程其实就是调用一个类中的方法,当作普通的方法的方式调用。并没有创建一个线程,程序中依旧只有一个主线程,必须等到run()方法里面的代码执行完毕,才会继续执行下面的代码,这样就没有达到写线程的目的。

下面我们通过一个很经典的题目来理解一下:

public class Test {
  public static void main(String[] args) {
    Thread t = new Thread(){
      public void run() {
        pong();
      }
    };
    t.run();
    System.out.println("ping");
  }

  static void pong() {
    System.out.println("pong");
  }
}

代码如图所示,那么运行程序,输出的应该是什么呢?没错,输出的是”pong ping”。因为t.run()实际上就是等待执行new Thread里面的run()方法调用pong()完毕后,再继续打印”ping”。它不是真正的线程。

而如果我们将t.run();修改为t.start();那么,结果很明显就是”ping pong”,因为当执行到此处,创建了一个新的线程t并处于就绪状态,代码继续执行,打印出”ping”。此时,执行完毕。线程t得到CPU的时间片,开始执行,调用pong()方法打印出”pong”。

如果感兴趣,还可以多加几条语句自己看看效果。

二、源码

那么他们本质上的区别在哪里,我们来看一下源码:

/**java
   * Causes this thread to begin execution; the Java Virtual Machine
   * calls the <code>run</code> method of this thread.
   * <p>
   * 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).
   * <p>
   * 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.
     *
     * 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();
      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 */
      }
    }
  }
  private native void start0();

可以看到,当一个线程启动的时候,它的状态(threadStatus)被设置为0,如果不为0,则抛出IllegalThreadStateException异常。正常的话,将该线程加入线程组,最后尝试调用start0方法,而start0方法是私有的native方法(Native Method是一个java调用非java代码的接口)。

我猜测这里是用C实现的,看来调用系统底层还是要通过C语言。这也就是为什么start()方法可以实现多线程。而调用run()方法,其实只是调用runnable里面自己实现的run()方法。

我们再看看Thread里run()的源码:

@Override
  public void run() {
    if (target != null) {
      target.run();
    }
  }

如果target不为空,则调用target的run()方法,那么target是什么:

/* What will be run. */
  private Runnable target;

其实就是一个Runnable接口,正如上面代码中new Thread的部分,其实我们就是在实现它的run()方法。所以如果直接调用run,就和一个普通的方法没什么区别,是不会创建新的线程的,因为压根就没执行start0方法。

三、实现

前面说了,继承Thread类和实现Runnable接口都可以定义一个线程,那么他们又有什么区别呢?
在《Java核心技术卷1 第9版》第627页提到。可以通过一下代码构建Thread的子类定义一个线程:

class MyThread extends Thread {
  public void run() {
    //do Something
  }
}

然后,实例化一个对象,调用其start方法。不过这个方法不推荐。应该减少需要并行运行的任务数量。如果任务很多,要为每个任务创建一个独立的线程所付出的代价太多,当然可以用线程池来解决。

实现Runnable接口所具有的优势:

1、避免Java单继承的问题

2、适合多线程处理同一资源

3、代码可以被多线程共享,数据独立,很容易实现资源共享

总结一下:

1.start() 可以启动一个新线程,run()不能

2.start()不能被重复调用,run()可以

3.start()中的run代码可以不执行完就继续执行下面的代码,即进行了线程切换。直接调用run方法必须等待其代码全部执行完才能继续执行下面的代码。

4.start() 实现了多线程,run()没有实现多线程。

以上所述是小编给大家介绍的Java中启动线程start和run方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Java线程中start和run方法全面解析

    自定义线程两种方法 自定义一个runnable接口的实现类,然后构造一个thread,即对thread传入一个runnable接口类. new一个thread或者写个thread子类,覆盖它的run方法.(new 一个thread并覆盖run方法实际上是匿名内部类的一种方式) 示例代码 public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.

  • Java线程的start方法回调run方法的操作技巧

    面试中可能会被问到为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法? Java 创建线程的方法 实际上,创建线程最重要的是提供线程函数(回调函数),该函数作为新创建线程的入口函数,实现自己想要的功能.Java 提供了两种方法来创建一个线程: 继承 Thread 类 class MyThread extends Thread{ public void run() { System.out.println("My thread is started.&qu

  • java中thread线程start和run的区别

    最近看到一个题目,代码如下: 复制代码 代码如下: public static void main(String args[]) {Thread t = new Thread() {public void run() {pong();}}; t.run();System.out.println("ping");}static void pong() {System.out.println("pong");} 问,结果会输出什么? 我运行了很多次,结果都是pong p

  • java基本教程之Thread中start()和run()的区别 java多线程教程

    Thread类包含start()和run()方法,它们的区别是什么?本章将对此作出解答.本章内容包括:start() 和 run()的区别说明start() 和 run()的区别示例start() 和 run()相关源码(基于JDK1.7.0_40) start() 和 run()的区别说明start() : 它的作用是启动一个新线程,新线程会执行相应的run()方法.start()不能被重复调用.run()   : run()就和普通的成员方法一样,可以被重复调用.单独调用run()的话,会在

  • java 线程中start方法与run方法的区别详细介绍

    线程中start方法与run方法的区别 在线程中,如果start方法依次调用run方法,为什么我们会选择去调用start方法?或者在java线程中调用start方法与run方法的区别在哪里?  这两个问题是两个非常流行的初学者级别的多线程面试问题.当一个Java程序员开始学习线程的时候,他们首先会学着去继承Thread类,重载run方法或者实现Runnable接口,实现run方法,然后调用Thread实例的start方法.但是当他拥有一些经验之后,他通过查看API文档或者其他途径会发现start

  • Java中启动线程start和run的两种方法

    一.区别 Java中启动线程有两种方法,继承Thread类和实现Runnable接口,由于Java无法实现多重继承,所以一般通过实现Runnable接口来创建线程.但是无论哪种方法都可以通过start()和run()方法来启动线程,下面就来介绍一下他们的区别. start方法: 通过该方法启动线程的同时也创建了一个线程,真正实现了多线程.无需等待run()方法中的代码执行完毕,就可以接着执行下面的代码.此时start()的这个线程处于就绪状态,当得到CPU的时间片后就会执行其中的run()方法.

  • 浅谈Java中Collections.sort对List排序的两种方法

    目录 一.Collections.sort的简单使用 二.问题提出 三.Comparable实现排序 四.Comparator实现排序 五.Comparable 与Comparator区别 一.Collections.sort的简单使用 说到List的排序,第一反应当然是使用Collections.sort,方便简单.下面实现一下~~ private void sortStrings() { List<String> list = new ArrayList<String>();

  • java中ThreadLocal取不到值的两种原因

    1.两种原因 第一种,也是最常见的一种,就是多个线程使用ThreadLocal 第二种,类加载器不同造成取不到值,本质原因就是不同类加载器造成多个ThreadLocal对象 public class StaticClassLoaderTest { protected static final ThreadLocal<Object> local = new ThreadLocal<Object>(); //cusLoader加载器加载的对象 private Test3 test3;

  • 详解Java Web如何限制访问的IP的两种方法

    前一阵子因为在做项目时碰到了这个功能,现在好好总结一下,至于为什么要限制IP访问,我就不多说了.然后百度了一下,现在主要有两种方式去限制IP访问,第一种是最简单的方便的,第二种是通过过滤器来限制访问.下面我简单介绍一下第一种方式,着重介绍第二种. 第一种方式(Tomcat配置项配置允许或限制IP访问) 这种是最简单的快捷的,主要就涉及Tomcat的server.xml配置. 第一步:找到server.xml文件在哪,在Tomcat的目录下的conf文件夹下. 第二步:打开server.xml文件

  • ASP.Net中利用CSS实现多界面的两种方法

    本文实例讲述了ASP.Net中利用CSS实现多界面的两种方法.分享给大家供大家参考.具体实现方法如下: 可以通过使页面动态加载不同CSS来实现多界面的效果: 方法一: 复制代码 代码如下: <%@page language="C#"%> <%@import namespace="System.Data"%> <script language="c#" runat="server"> publ

  • Flask中获取小程序Request数据的两种方法

    Flask中获取小程序Request数据的两种方法 作为后端屌对于前端确实讳莫如深,JS中的类型Object竟然不能直接通过POST传入到后台Flask中,您会发现获取的是[object Object],这货在Flask中只是个Str,哈哈... 于是,开始寻求解决方案: 方案一,通过GET或者POST传参,将Object转换为Str请求数据: 1.在util.js中定义一个JS函数: function json2Form(json) { var str = []; for (var p in

  • java 在file的尾部添加数据的两种方法总结

    java 在file的尾部添加数据的两种方法总结 问题描述:   在文件的末尾追加内容 方法1:利用RandomAccessFile类  1.将randomAccessFile模式设置为rw  2将randomAccessFile移动(seek)到文件末尾  3追加数据  4关闭流 方法2:利用FileWriter类  1.将FileWriter构造方法第二个参数置为true.表示在尾部追加  2追加数据  3.关闭流 实现代码: package cn.com; import java.io.F

  • python3中获取文件当前绝对路径的两种方法

    方法1: import sys print(sys.argv) 得到文件当前绝对路径字符串的一个列表 ['D:/pycharm/PracticeProject/ClientServerNetworking.py'] 方法2: import os print(os.getcwd()) print(os.listdir()) print(os.path.join(os.getcwd(),os.listdir()[1])) D:\pycharm\PracticeProject ['.idea', 'C

  • JavaScript检查数据中是否存在相同的元素(两种方法)

    这里是两个用于数组中查找重复元素的demo,可以看看啦 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <input type="text" id="Values" style=

  • Java实现对象按照其属性排序的两种方法示例

    本文实例讲述了Java实现对象按照其属性排序的两种方法.分享给大家供大家参考,具体如下: 有时候需要对对象列表或数组进行排序,下面提供两种简单方式: 方法一:将要排序的对象类实现Comparable<>接口. 首先,创建学生类,我们将根据学生成绩对学生进行排序: /** * 学生类 */ class Student implements Comparable<Student>{ String name; int age; int score; public Student(Stri

随机推荐