Thread与Runnable对比

java多线程的实现方式最基本的有两种,继承Thread和实现Runnable接口。但实则不然,从跟本上来说只有一种方式,那就是实例化Thread,重写其中的run方法。当然这只是准备状态,真正启动线程需要调用该线程的start方法进行启动。

  1. 继承Thread
public class Demo1 {
    public static void main(String[] args) {
        Conductor conductor = new Conductor();
        conductor.start();
    }
    static class Conductor extends Thread{
        @Override
        public void run() {
            System.out.println("卖票了!!!");
        }
    }
}

这部分应该来说很好理解,Conductor继承了Thread并重写了其中的run方法,run方法就是该线程启动后需要执行的某段代码,最后在主线程创建一个Conductor,调用start开启线程。

  1. 实现Runnable接口
public class Demo2 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Conductor());
        thread.start();
    }
    static class Conductor implements Runnable {
        @Override
        public void run() {
            System.out.println("卖票了!!!");
        }
    }
}

通过实现Runnable接口,并实现run方法,最后创建实例作为Thread构造函数的参数传入同样可以实现开启新线程。
既然有两种方式,那我们使用时总该在这两种方式中做出选择,现在来看看两种实现方式的优缺点。

  • 在java里面只允许进行单继承多实现,如果使用使用继承Thread的方式,那该类就无法继承其他类,而实现Runnable接口则可以避免单继承的限制。
  • 实现Runnable可以实现多线程的资源共享,而继承Thread则达不到这种效果。
  • 在Thread中进行线程的控制逻辑,在Runnable的实现类中运行业务逻辑,分工较为明确,解耦更彻底一些。

看到这里可能大家会有个疑问,为什么是调用Thread.start()而不是其中的run方法呢?这里可以稍微看一下其中的源码。

  1. Thread 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();
            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 */
            }
        }
    }

这段代码的大体逻辑是这样:

  1. A zero status value corresponds to state “NEW”.通过threadStatus的值判断线程状态,若不为0说明已启动线程,执行start方法就抛出异常
  2. 把执行该方法的线程加入到线程的group中
  3. 调用另一个名为start0的方法

start最终调用了start0的方法而不是run,现在我们看一下start0方法:

private native void start0();

什么东西都没有,甚至连注解都没有,不过值得注意的是这个关键字native,就是说它是一个native方法,也叫做JNI方法,最终这个方法将进入JVM中执行。嗯?那在JVM中执行了什么,我们暂且不去研究,回到start方法,注意一下那一大串注解,开头就告诉了我们Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread.意思就是start方法是现成的执行者,JVM会执行该线程的run方法。而我们知道start最终是调用了start0方法,start0方法又在JVM中,大体可以推测start0方法中的逻辑会直接或间接调用run()方法。
enter description here

  1. Thread run方法分析
    我们先看Thread这个类:
public class Thread implements Runnable {
    ...
}

从这里我们发现Thread实际上也是实现了Runnable,也就是说Thread中的run方法也是重写了Runnable的,而我们通过实现Runnable接口来实例一个线程是通过Thread的构造函数,所以这里我们可以先从Thread的构造函数进行入手。

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

在需要传入Runnable的构造方法中发现,啥也没做,只是调用了另一个方法init,下面看一下找到最终调用的init.这里面有一行代码

this.target = target;

而this。target中的target正是Thread的成员变量

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

此时Thread中target就被设置为构造函数方法中传入的target,即为Runnable的实现。
最后再看一下run方法:

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

当传入了target,就会执行target中的run方法。同样的,如果是通过继承Thread实例化,那就会执行继承是重写的run方法。
总得来说大致流程如下:
enter description here

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值