zoukankan      html  css  js  c++  java
  • 实现Runnable接口和继承Thread类的区别

      实现多线程编程主要有两种方式:一种是继承Thread类,一种是实现Runnable接口。这两种方式在运行结果上其实并没有多大的差别,但是应用场景和内部执行流程还是有区别的。

      其实Thread类也是实现了Runnable接口的类,这点通过其源码就可以看出来:

    public
    class Thread implements Runnable {
            ……
    }

      而Runnable接口只定义了一个方法,那就是run方法,

    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();
    }

      任何实现了Runnable接口的方法都要重写该方法,run方法内部正是包含了线程要执行的任务代码。那么既然Thread类也实现了Runnable接口,所以也必然实现了run方法,那么我们就看一下Thread类重写的run方法吧:

    /**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

      通过此方法的注释可以看出大致的含义:如果当前线程对象是由单独的Runnable接口对象构建的,那么将会调用Runnable接口对象的run方法,否则这个方法不执行任何操作并返回。那么为何会有Runnable接口对象创建了当前的线程对象呢?

      这个通过查阅API可以知道,Thread类有五个构造方法可以通过Runnable接口对象创建一个Thread对象:

      而这些可以传入Runnable接口对象的Thread构造方法,在其内部执行的都是init方法:

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    Thread(Runnable target, AccessControlContext acc) {
        init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
    }
    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }
    public Thread(ThreadGroup group, Runnable target, String name) {
        init(group, target, name, 0);
    }
    public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize) {
        init(group, target, name, stackSize);
    }

      这些init方法最终都指向同一个私有的init方法,这个方法就是用于初始化线程对象,最核心的代码如下面所示,就是将构造方法中Runnable接口对象赋予成员变量target,然后JVM会执行Thread类的run方法,也就是上面源码显示的,先对成员变量target进行判空,如果对象不是null,就执行target的run方法,这个run方法也就是我们自定义的实现Runnable接口的类中实现的run方法。

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
            ……
        
        this.target = target;
        
            ……
    }

      对于继承Thread类的子类来说,它们需要重写上面那个Thread类中的run方法,将任务写到这个run方法中,然后在创建Thread类的子类对象,其子类对象再执行start方法就启动了一个线程,线程启动后会自动调用线程对象中的run方法,这个run方法就是我们在子类中重写的run方法,它包含了线程对象要执行的任务。

    总结一下,进行多线程编程的两种方式:

      1、继承Thread类

        定义一个类,继承Thread类,重写run方法,run方法包含了线程执行的任务;

        创建这个类对象,然后通过对象调用start方法启动线程,直接执行的就是子类中重写的run方法。

        这种方式有个局限性,就是单继承局限性,一旦继承了Thread类,就不能继承其他类了。

      2、实现Runnable接口

        定义一个类,实现Runnable接口,重写run方法,run方法包含了线程执行的任务;

        创建这个类对象,然后再创建相应数量的Thread对象,作为代理,将Runnable接口对象传入Thread对象中,然后调用Thread对象的start方法启动线程。启动的过程就是先初始化线程对象将传入的Runable接口对象赋予target成员变量,然后执行run方法调用Runnable接口对象的run方法。

        这种方法避免了单继承局限性,灵活方便,而且创建一个线程对象,可以被多个线程同时使用。相当于一份资源被多个线程共享。比如下面这个示例:

    创建一个Runnable接口实现类:

    public class BServer extends AServer implements Runnable {
    ​
        private int source = 5;
    ​
        public void b_save_method(){
    ​
            System.out.println(Thread.currentThread().getName()+"消耗了资源:" + (source--));
        }
    ​
        @Override
        public void run() {
            b_save_method();
        }
    }

    定义一个线程对象和多个Thread对象:

    public class Run5 {
    ​
        public static void main(String[] args) {
    ​
            BServer bServer = new BServer();
    ​
            Thread thread1 = new Thread(bServer);
            Thread thread2 = new Thread(bServer);
            Thread thread3 = new Thread(bServer);
            Thread thread4 = new Thread(bServer);
            Thread thread5 = new Thread(bServer);
    ​
            thread1.start();
            thread2.start();
            thread3.start();
            thread4.start();
            thread5.start();
        }
    }

    运行结果如下:

    Thread-0消耗了资源:5
    Thread-4消耗了资源:3
    Thread-1消耗了资源:4
    Thread-2消耗了资源:2
    Thread-3消耗了资源:1

    可以看出一个Runnbale接口对象可以被多个线程同时使用。

  • 相关阅读:
    linux远程文件、目录操作
    make update-api的使用
    android4.1设置系统 默认方向
    NAIPC2018-K-Zoning Houses
    ICPC2017 Urumqi
    牛客多校第十场-D- Rikka with Prefix Sum
    杭电多校第八场-A-Character Encoding
    杭电多校第七场-J-Sequence
    ConvexScore
    异或序列
  • 原文地址:https://www.cnblogs.com/yxym2016/p/13129819.html
Copyright © 2011-2022 走看看