• 实现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接口对象可以被多个线程同时使用。

  • 相关阅读:
    扫面线模板
    (动态规划、栈)leetcode 84. Largest Rectangle in Histogram, 85. Maximal Rectangle
    tmux 常见命令汇总
    leetcode 221
    leetcode 319 29
    (贪心)leetcode 392. Is Subsequence, 771. Jewels and Stones, 463. Island Perimeter
    leetcode 982 668
    Python import 同文件夹下的py文件的函数,pycharm报错
    Windows里Anaconda-Navigator无法打开的解决方案
    Windows下 gpu版 Tensorflow 安装
  • 原文地址:https://www.cnblogs.com/yxym2016/p/13129819.html
Copyright © 2020-2023  润新知