Thread 中的run() 与start() 方法

本文详细解释了Java中线程的启动方式,对比了通过继承Thread类与实现Runnable接口两种方式下,调用run()与start()方法的区别,并强调了start()方法才是正确启动线程的方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

将类声明为 Thread 的子类是创建线程的方式之一,如果我们想要在线程内定义任务那么就要重写Thread 中的run() 方法。start() 方法使我们定义的线程执行,我们已经将线程的任务定义在了run() 方法中为什么run() 方法不是执行线程的方法而是start() 方法呢?当时学线程的时候并没有多考虑这个问题,由于以前有一些误解,现在就写出来供自己与大家参考。

      我定义了一个Thread 的子类ThreadTest ,在该子类中重写了run() 方法,在run() 方法中用于输出当前正在执行的线程引用,为了达到效果我让线程等待了100ms。在创建两个线程对象thread1 与thread2 后直接调用了它们的run() 方法,我们希望得到的结果是thread1 与thread2 共同抢占CPU 以达到交错输出,但是从输出的结果看出在执行的始终是主线程(main)。

public class ThreadTest  extends Thread{
    @Override
    public void run() {
        for(int i = 0; i < 5; i++){
            try {
                Thread.sleep(100);
                //Thread.currentThread() 返回对当前正在执行的线程对象的引用
                System.out.println(Thread.currentThread());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {

        TestThread thread1 = new TestThread();
        TestThread thread2 = new TestThread();

        thread1.run();
        thread2.run();
    }
}

输出:
Thread[main,5,main]
Thread[main,5,main]
Thread[main,5,main]
Thread[main,5,main]
Thread[main,5,main]
Thread[main,5,main]

      下面我们用和上面同样的代码,不再调用ThreadTest 的run() 方法,这次调用start() 方法,看看输出结果会是什么?从下面的结果可以看出这次输出的不再是主线程,而是线程0与线程1 (但是执行的线程也包括主线程)。

public class ThreadTest extends Thread{
    @Override
    public void run() {
        for(int i = 0; i < 3; i++){
            try {
                Thread.sleep(100);
                //Thread.currentThread() 返回对当前正在执行的线程对象的引用
                System.out.println(Thread.currentThread());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {

        TestThread thread1 = new TestThread();
        TestThread thread2 = new TestThread();

        thread1.start();
        thread2.start();
    }
}

输出
Thread[Thread-1,5,main]
Thread[Thread-0,5,main]
Thread[Thread-0,5,main]
Thread[Thread-1,5,main]
Thread[Thread-1,5,main]
Thread[Thread-0,5,main]

      通过执行run() 方法与start() 方法可以知道Thread 并不是通过run() 方法执行线程的而是通过start() 方法执行线程,我们只是把线程的任务定义在了run() 方法中,但是它并不是执行任务的方法。我们知道创建执行线程任务还有一种方式,就是实现Runnable 接口。下面我们就来看一下Runnable 中的run() 方法,为了更好的对比我在run() 方法中定义了和上面一样的任务。通过上面的了解我们知道执行run() 方法并不能让线程执行,所以输出的仍然是主线程的引用。

public class RunnableTest implements Runnable {
    @Override
    public void run() {
        for(int i = 0; i < 3; i++){
            try {
                Thread.sleep(100);
                System.out.println(Thread.currentThread());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {

        RunnableTest runnableTest1 = new RunnableTest();
        RunnableTest runnableTest2 = new RunnableTest();

        runnableTest1.run();
        runnableTest2.run();
    }
}

输出
Thread[main,5,main]
Thread[main,5,main]
Thread[main,5,main]
Thread[main,5,main]
Thread[main,5,main]
Thread[main,5,main]

      通过看Runnable 接口的源码你就会知道这个接口只定义了一个run() 方法,这也说明着Runnable 并不能创建线程实例,那么怎么执行在其中定义的任务呢?想要创建线程实例还不简单吗,直接new 一个Thread 对象不就好了吗。Thread类 提供了一个 Thread(Runnable target) 构造器,它允许我们传入一个Runnable 子类的实例,下面我们就来这么做。

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

      重新定义了两个Thread 类实例并将RunnableTest 对象传入Thread 类中的构造器,然后由线程实例thread1 与thread2 执行线程任务,我们看到正如我们所希望的那样打印着执行线程的引用。

public class RunnableTest implements Runnable {
    @Override
    public void run() {
        for(int i = 0; i < 3; i++){
            try {
                Thread.sleep(100);
                System.out.println(Thread.currentThread());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {

        RunnableTest runnableTest1 = new RunnableTest();
        RunnableTest runnableTest2 = new RunnableTest();
        Thread thread1 = new Thread(runnableTest1);
        Thread thread2 = new Thread(runnableTest2);

        thread1.start();
        thread2.start();
    }
}

输出
Thread[Thread-1,5,main]
Thread[Thread-0,5,main]
Thread[Thread-1,5,main]
Thread[Thread-0,5,main]
Thread[Thread-1,5,main]
Thread[Thread-0,5,main]

总结:
      Thread 中的run() 方法只是定义了线程的任务但是它不负责执行该任务,如果你调用该方法那么执行的只是主线程,就像某一个对象实例调用它自己的方法一样。
      Thread 中的start() 方法才是执行线程的方法,在执行该方法时,Java 虚拟机会调用该线程的run() 方法,从而达到并发执行的目的。在这里你需要注意的是多次启动一个线程是不安全的,特别是线程执行结束后,不要再重新启动,这点要特别注意。
      在这里也通过实现Runnable 接口来定义线程任务,需要注意的是Runnable 接口的实现并不能创建线程实例,当你想要执行其中定义的任务时,你需要将该子类的实例作为参数传入Thread 中的构造器,通过Thread 类的实例来执行线程任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值