首页 > 其他分享 >Runnable和Thread比较

Runnable和Thread比较

时间:2023-05-03 20:46:49浏览次数:34  
标签:Runnable run Thread start 线程 方法 比较

(一)Thread类实现了Runnable接口吗?

在线程使用过程中,我们肯定会用到Runnable与Thread,前者的实现方式是实现其接口即可,后者的实现方式是继承其类。两者实现方式带来最明显的区别就是,由于Java不允许多继承,因此实现了Runnable接口可以再继承其他类,但是Thread明显不可以。

我们看看源码中对与Thread类的部分声明

    public class Thread implements Runnable {
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }

    private volatile String name;
    ...

Thread 类是实现了Runnable接口的。

(二)实现多线程的两种方法

第一种方法:继承Thread类

方法步骤总结:

  1. 定义一个类继承Thread;
  2. 重写Thread类中的run方法,将需要被多线程执行的代码存储到该run方法当中。
  3. 建立Thread类的子类创建线程对象。
  4. 直接调用子类从Thread类继承的start方法,开启一个线程(调用该线程的run方法)。

第二种方法:实现Runable接口

Thread类有一个Thread(Runnable target)构造方法,在Runable接口类中只有一个run()方法。

当使用Thread(Runnable target)方法创建线程对象时,需要为该方法传递一个实现 Runnable接口的对象,这样创建的线程将调用那个实现了Runnable接口类对象中的run()方法作为其运行代码,而不再是调用Thread类中的run方法了。

方法步骤总结:

  • 定义一个类实现Runnable接口,覆盖Runnable接口中的run方法,将线程要运行的代码存放在该run方法中;
  • 通过Thread类建立线程对象,将Runnable接口的子类实例对象作为实际参数传递给Thread类的构造方法。
Thread readerThread = new Thread(new IncomingReader());
readerThread.start();

class IncomingReader implements Runnable{
...
}

两种方式区别

  • 继承Thread: 线程代码存放Thread子类run方法中,且该run方法被调用。
  • 实现Runnable:线程代码存在实现了Runnable类接口的对象的run方法中,且该run方法被调用。

注意:启动一个新的线程,不是直接调用Thread子类的对象的run方法,而是调用Thread子类对象的start方法

start方法是从Thread类中继承的方法,Thread类对象的start方法将产生一个新的线程,并在该线程上运行该Thread类对象中的run方法。

根据面向对象的多态性可知,在该线程上实际运行的是我们编写的那个类(Thread的子类)对象中的run方法。

(三)Runnable和Thread比较

如上所述,Runnable相比Thread存在明显的优点,同时也是两者最大的区别。这点就不再做阐述,这里对于网络很多文章中存在的明显的错误文字总结进行一下论证:

Runnable可以实现多个相同的程序代码的线程去共享同一个资源,而Thread不可以?

我们以实际的代码样例来论证这个观点:

package test;

/**
 * Thread 实现资源共享
 * @author itbird
 *
 */
public class Test2 {

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        new Thread(t1, "线程1").start();
        new Thread(t1, "线程2").start();
    }

    public static class MyThread extends Thread {
        private int total = 10;

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                synchronized (this) {
                    if (total > 0) {
                        try {
                            Thread.sleep(100);
                            System.out.println(Thread.currentThread().getName() + "卖票---->" + (this.total--));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

Thread实现共享同一个资源

package test;

/**
 * Runnable 实现资源共享
 * @author itbird
 *
 */
public class Test3 {

    public static void main(String[] args) {
        MyRunable t1 = new MyRunable();
        new Thread(t1, "线程1").start();
        new Thread(t1, "线程2").start();
    }

    public static class MyRunable implements Runnable {
        private int total = 10;

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                synchronized (this) {
                    if (total > 0) {
                        try {
                            Thread.sleep(100);
                            System.out.println(Thread.currentThread().getName() + "卖票---->" + (this.total--));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

Runnable实现共享同一个资源

经过实际样例代码编写以及运行结果对比,我们知道很多人说的Thread类不能共享资源,其实并不是不能,只是不适合

其实我们从Thread源码中也可以看到,当以Thread方式去实现资源共享时,实际上源码内部是将thread向下转型为了Runnable,实际上内部依然是以Runnable形式去实现的资源共享

(四)Runnable为什么不可以直接run

多线程原理:相当于玩游戏机,只有一个游戏机(cpu),可是有很多人要玩,于是,start是排队!等CPU选中你就是轮到你,你就run(),当CPU的运行的时间片执行完,这个线程就继续排队,等待下一次的run()。

调用start()后,线程会被放到等待队列,等待CPU调度,并不一定要马上开始执行,只是将这个线程置于可动行状态。然后通过JVM,线程Thread会调用run()方法,执行本线程的线程体。先调用start后调用run,这么麻烦,为了不直接调用run?就是为了实现多线程的优点,没这个start不行。

  1. start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程
  2. run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的

解释到这里,相信各位看官心里有种“了然大明白”的感觉,runnable其实相对于一个task,并不具有线程的概念,如果你直接去调用runnable的run,其实就是相当于直接在主线程中执行了一个函数而已,并未开启线程去执行,所以显而易见,如果你在代码中直接通过这种方式run了一个runnable,明显您的程序的主线程就直接悲催了,各种资源不足现象的崩溃日志会接踵而至,而且遇到这种问题的时候,如果没有一定的研发经验和坚持,很有可能采取错误的解决策略。

 

标签:Runnable,run,Thread,start,线程,方法,比较
From: https://www.cnblogs.com/imreW/p/17369637.html

相关文章

  • 关于跨世界观比较战力的一些想法
    1.表现关于一个人物在同个状态下全力做出的若干个表现,正确的做法是取上限/去掉极端上限取普通表现。(比如一个人物设定上有爆星级,)这也是论战圈常用的做法。因为这样子可以公平地比较战力。不能取下限,因为(当然对于唐三除外)......
  • 比较算法(1)
    1、介绍需求:有时候需要比较两个文本,看有什么异同。在渗透过程中分析响应变化很实用,可以快速定位不同区域,比如在xss分析过程,或者定位一次性token另一个场景,是对文件与文件的字节进行比较,用于学习文件结构,以及分析图片木马、加壳、后缀名修改等操作的影响比较算法,将两个文本分......
  • ThreadLocal 详解
    1.ThreadLocal是什么?/为什么要使用ThreadLocal?ThreadLocal是什么?ThreadLocalThreadLocal类位于java.lang包下,由JDK包提供。如果创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的本地副本,多个线程操作这个变量的时候,其实是在操作自己本地内存里的变......
  • 结构体内嵌比较函数bool operator < (const node &x) const {}
    structnode{intl,r;booloperator<(constnode&a)const{returnr<a.r;}}a[maxn];使用sort时,如果这么定义节点,说明节点要按照从小到大排序(sort中默认从小到大排序);但是同样的代码,如果使用优先队列,这么写就说明节点要按照从大到小排序(优先队列默......
  • cpp multi thread std::lock_guard,mutex
    #include<atomic>#include<chrono>#include<cmath>#include<condition_variable>#include<ctime>#include<fstream>#include<functional>#include<future>#include<iomanip>#include<iostream&g......
  • 一个比较强大的提供各种形状的ImageView
    github上比较老的项目了,但是还是比较好用的。各种形状总有一个满足你。在自己的项目中使用,最好不要将所有的类都导入进来,取自己需要的就可以了。<com.github.siyamed.shapeimageview.BubbleImageViewandroid:id="@+id/image"android:layou......
  • RTThread的初始化宏(备忘录)
    RTThread的初始化流程方便后续查找.一.初始化接口初始化顺序接口描述1INIT_BOARD_EXPORT(fn)硬件的初始化,此时调度器还未启动2INIT_PREV_EXPORT(fn)主要是用于纯软件的初始化、没有太多依赖的函数3INIT_DEVICE_EXPORT(fn)外设驱动初始化相关,比如网卡设备......
  • cpp multi thread sync via std::atomic<bool>
    #include<atomic>#include<chrono>#include<cmath>#include<condition_variable>#include<ctime>#include<fstream>#include<functional>#include<future>#include<iomanip>#include<iostream&g......
  • 六种比较方法使用a < b一种方式表示出来
    a>b等价于b<aa>=b等价于!(a<b)即a不小于ba<=b等价于!(b<a)即b不小于aa==b等价于!(a<b)&&!(b<a)夹逼原理a!=b等价于(a<b)||(b<a)在c++中重载运算符时,只需要重载<小于号就可以了参考:https://www.acwing.com/file_system/file/......
  • 开发中用的比较顺手的截图工具(windows+mac)
    让Snipaste帮你提高工作效率Snipaste是一个简单但强大的截图工具,也可以让你将截图贴回到屏幕上!下载并打开Snipaste,按下 F1 来开始截图,再按 F3,截图就在桌面置顶显示了。就这么简单!你还可以将剪贴板里的文字或者颜色信息转化为图片窗口,并且将它们进行缩放、旋转、翻转、设为半......