java中的Runnable接口和Thread类详解 您所在的位置:网站首页 用于创建线程类的接口有哪些方法 java中的Runnable接口和Thread类详解

java中的Runnable接口和Thread类详解

2024-07-16 20:38| 来源: 网络整理| 查看: 265

Runnable接口和Thread线程类源码

Runnable是一个接口,接口内只声明了一个run()方法,声明如下:

public interface Runnable { /** * When an object implementing interface Runnable is used * to create a thread, starting the thread causes the object's * run method to be called in that separately executing * thread. *

* The general contract of the method run is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }

英文处的注释大概意思就是:当实现Runnable接口的对象用于创建线程时,启动线程会在单独执行的线程中调用对象的run()方法。

注:这里的注释也就大概说出了通过实现Runnable接口来创建接口的过程。首先实现了Runnable接口的实现类对象不是一个线程,它只是创建线程时需要的一个目标对象,当用这个对象来创建线程时,启动线程,也就是执行了实现类里实现的run()方法。

Thread类是线程类,它实现了Runnable接口,也就是实现了run()方法。另外,所有的线程类方法,如start()、exit()方法都在这个类中定义。下面看一下它的声明

public class Thread implements Runnable { ... public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } ... public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } ... 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 */ } } } ... @Override public void run() { if (target != null) { target.run(); } } ... }

这里我只粘出了其中的4个方法,还有很多方法,大家可以自行查看。这里只是让大家看到实现关系。

Runnable接口和Thread类创建线程实战

1.首先是通过实现Runnable接口来创建线程

我们先来定义一个实现类

public class ThreadTest implements Runnable{ private String threadName; public ThreadTest(){ } public ThreadTest(String threadName){ this.threadName = threadName; } @Override public void run(){ System.out.println(this.threadName + " ThreadTest run..."); try { Thread.sleep(2000);//因为sleep方法是Thread类的静态方法,所以要加上Thread来调用 } catch (InterruptedException e) { e.printStackTrace(); }finally{ System.out.println(this.threadName + " ThreadTest run over."); } } }

这里定义了一个实现类,有一个私有字段,两个构造方法,一个run()实现方法。

下面我们来看一下怎么去用这个类去创建一个线程

public static void main(String[] args) { Runnable runnableImpl = new ThreadTest(); Thread thread1 = new Thread(runnableImpl); thread1.start(); } 执行结果: null ThreadTest run... null ThreadTest run over.//因为我们调用了无参构造,所以类中的threadName字段没有赋值,而java中会给成员变量赋默认值,String类型的默认值是null

这是一个典型的用Runnable接口的实现类对象来创建线程,通过我上面贴的Thread类源码中的

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

这个构造方法,我们要创建一个线程对象,就得先创建出一个Runnable实现类对象,所以我们又先一步以Runnable接口的实现类ThreadTest的构造方法创建了一个对象。

总结:通过实现Runnable接口来创建线程的过程

创建一个Runnable接口实现类对象将第一步创建的对象作为目标对象用来创建一个线程类对象最后就可以调用线程类对象的方法来实现“启动”等一系列线程操作

上面讲的是最常规的通过Runnable接口实现类创建线程的方法,在实际情况中,我们会做出一些变形,下面我们来看一下

public static void main(String[] args) { Runnable runnableImpl = new ThreadTest("接口一"); Thread thread1 = new Thread(runnableImpl); thread1.start(); ThreadTest runnableImpl2 = new ThreadTest("接口二"); Thread thread2 = new Thread(runnableImpl2); thread2.start(); Thread thread3 = new Thread(new ThreadTest("接口三")); thread3.start(); Thread thread4 = new Thread(new Runnable(){ public void run(){ System.out.println("匿名内部类 run..."); System.out.println("匿名内部类 run over."); } }); thread4.start(); new Thread(new Runnable(){ public void run(){ System.out.println("匿名内部类2 run..."); System.out.println("匿名内部类2 run over."); } }).start(); } 执行结果: 接口一 ThreadTest run... 接口二 ThreadTest run... 接口三 ThreadTest run... 匿名内部类 run... 匿名内部类 run over. 匿名内部类2 run... 匿名内部类2 run over. 接口三 ThreadTest run over. 接口一 ThreadTest run over. 接口二 ThreadTest run over.

上面就是通过实现Runnable接口来创建线程的几种变形,其实不管怎么变,执行的过程都是我上面总结的那三步。这里就不一一解释了,大家有不懂得或是我文中有哪些问题都可以留言问我。

注:这里说一点,执行结果的顺序可以不一样,因为实际的线程执行中,调用顺序受多方面的影响,执行的start()方法,只是进入了就绪队列,想要真正执行必须要等cpu调用,这一部分本文中不涉及,读者可自行百度。

2、通过继承Thread类创建线程

Thread类是线程类,它可以直接new出对象,就创建了一个线程,但是这样并没有什么实际意义

public static void main(String[] args) { Thread thread = new Thread(); thread.start(); }

这里new出了一个线程对象,然后执行start()方法进入就绪队列,等待cpu调用,执行Thread类里实现的run()方法

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

但是我们查看Thread类中的run()方法可以知道,它执行的是Runnable接口的实现类对象中的run()方法,但是我们并没有给它传这个对象,所以这里默认对象为空,也就是什么都不执行

下面我们来看一个继承类

public class ThreadTest extends Thread{ private String threadName; public ThreadTest(){ } public ThreadTest(String threadName){ this.threadName = threadName; } @Override public void run(){ System.out.println("ThreadTest run..."); try { sleep(2000);//因为继承了Thread类,所以可以直接调用Thread类的方法 } catch (InterruptedException e) { e.printStackTrace(); }finally{ System.out.println("ThreadTest run over."); } } }

同样还是刚才那个类,但是此时不再是实现Runnable接口了,而是直接继承了Thread线程类,所以这个类现在也是一个线程类,它可以之间new一个线程对象。我们同样给它定义了一个字段和两个构造方法。

通过继承Thread类创建线程的一般方法

public static void main(String[] args) { Thread thread = new ThreadTest(); thread.start(); } 执行结果: null ThreadTest run... null ThreadTest run over.

通过前面的介绍,相信大家都能看懂这个线程的创建,我们同样来总结一下:

总结:通过继承Thread类创建线程

因为继承了Thread类,所以此时的继承类已经是一个线程类了,所以可以直接new出一个新线程对象然后就可以调用“启动”等一系列线程类的方法了

通过总结很容易看出第二种方法比第一种方法少了一部创建Runnable接口的实现类对象,下面读者就可以自行写一下这种方法创建线程的变形,然后比对一下我下面写的

public static void main(String[] args) { ThreadTest thread1 = new ThreadTest("thread1"); thread1.start(); new ThreadTest("thread2").start(); Thread thread3 = new Thread(){ public void run(){ System.out.println("匿名内部类 Thread3 run..."); try { sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }finally{ System.out.println("匿名内部类 Thread3 run over."); } } }; thread3.start(); new Thread(){ public void run(){ System.out.println("匿名内部类 Thread4 run..."); try { sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }finally{ System.out.println("匿名内部类 Thread4 run over."); } } }.start(); } 执行结果: thread1 ThreadTest run... 匿名内部类 Thread3 run... thread2 ThreadTest run... 匿名内部类 Thread4 run... 匿名内部类 Thread4 run over. thread1 ThreadTest run over. 匿名内部类 Thread3 run over. thread2 ThreadTest run over.

从上面的运行结果就可以看出,线程的执行顺序并不是main函数里执行的顺序

整理与思考

整理的话可以看上面每个的小结,这里主要思考一个问题。

当我们向一个继承了Thread类的线程类传入一个实现了Runnable接口的实现类对象,创建一个线程时,执行这个线程是会调用继承类里重写的run()方法还是会调用实现类里的run()方法?大家可以动手实践一下,然后思考一下为什么,留言告诉我哦!

注:文中有不对或不足的地方,欢迎留言告诉我哦

温故而知新

多年过去,多线程相关涉猎少之又少,已经全然忘记初衷,回顾前著重新拾起

其实文中讲解的两种线程创建方式要结合起来看,如当初我们学习历史,东方历史与西方历史常常分开学习,以致于今日热搜说到爱因斯坦曾给陈独秀求情都震惊不已,这两人竟是同一时代的人!!!

Thread类其实是实现了Runnable接口的,所以Thread类中的run()方法是实现的Runnable接口定义的run()方法,我们知道线程中的run()方法写的是放入线程中执行的具体逻辑代码,所以肯定是不能事先定义好的,只有使用时才知道,所以我们在程序中使用线程来执行任务时需要继承Thread类来重写其中的run()方法,在run()方法中写上我们需要线程帮我们执行的代码。

那我们再回过头来看看Thread类里实现的run()方法是怎么写的

... /* What will be run. */ private Runnable target; ... /** * If this thread was constructed using a separate * Runnable run object, then that * Runnable object's run method is called; * otherwise, this method does nothing and returns. *

* Subclasses of Thread should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } }

Thread类实现的run()方法是去执行target的run()方法,target是Thread类定义的一个成员变量,指向的是一个实现了Runnable接口的实例对象,这个实例对象可以通过Thread类的构造函数传入,就是文中第一种创建线程的方法

到此我们回顾并再次总结了两种创建线程的方法,回答一下三年前留的问题!!!

通过这次的总结我相信能更轻松的回答这个问题,首先继承了Thread类后我们肯定是要重写run()方法写入我们想要线程帮我们执行的代码的(你可以不重写,当然那就没意义了),那我通过继承类来开启线程就执行的是我重写后的run()方法,而不是Thread类中写的target的run()方法,所以问题的答案就是执行重写后的方法。

什么,那传进去的Runnable对象呢?

其实Runnable对象我们不一定传的进去了哈,继承了Thread类的子类构造方法一般都不会再写接收Runnable对象,自己都实现了run()方法,还传干什么。那要是子类写了接收Runnable对象呢?那也没什么用,Thread类中的run()写的确实是执行target中的run()方法,可是你不是在子类里重写了Thread类的run()方法了嘛,那就跟target没什么关系了。

最后说一句:通过实现Runnable接口来创建线程就是函数式编程的思想,将行为作为参数传递



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有