java:Runnable和Thread接口的关系

时间:2010-03-09 00:59:32

标签: java interface

我意识到必须声明方法run(),因为它在Runnable接口中声明。但是,当这个类运行时,如果没有对特定包的导入调用,如何允许Thread对象,我的问题就来了? runnable如何知道关于Thread或其方法的任何信息? Runnable接口是否扩展了Thread类?显然我不太了解接口。提前谢谢。

    class PrimeFinder implements Runnable{
         public long target;
         public long prime;
         public boolean finished = false;
         public Thread runner;
         PrimeFinder(long inTarget){
              target = inTarget;
              if(runner == null){
              runner = new Thread(this);
              runner.start()
         }
    }
    public void run(){

    }
}

6 个答案:

答案 0 :(得分:4)

在这种情况下,我喜欢将接口视为合同。通过说您的类实现Runnable,您明确声明您的类遵守Runnable合约。这意味着其他代码可以创建类的实例并分配给Runnable类型:

Runnable r = new PrimeFinder();

此外,通过遵守此合同,您可以保证调用您的类的其他代码可以期望找到Runnable实现的方法(在本例中为run())。

答案 1 :(得分:3)

不。线程在lava.lang包中,所以它是隐含的导入。

而且:Thread知道Runnable。

这就是Thread收到Runnable(this实现Runnable)并在其自己的执行线程中调用其方法run()的原因。

线程保留对您实现的Runnable的引用:

public Thread(Runnable runnable) {
   this.myRunnable = runnable;
}

private Runnable myRunnable;

Thread类的start方法可能如下所示:

public void start() {
  // do weird stuff to create my own execution and...
  myRunnable.run();
}

答案 2 :(得分:2)

是的,Thread实施Runnable

由于API引用状态,runnable接口旨在为希望在活动时执行代码的对象提供通用协议

您感到困惑,因为在Java中有两种方法可以实现这种并发:

  • 您可以扩展Thread类覆盖默认的run方法,然后以类似于new MyThread().start()
  • 的方式调用该线程
  • 您可以编写一个实现Runnable接口的类,并以类似的方式启动它:new Thread(new MyRunnable()).start()

这些方法 IDENTICAL 。实际上,类run的{​​{1}}方法通常会调用附加到线程的Thread对象的run方法,否则返回。

拥有Runnable界面需要什么?它很有用,因为它声明了一个让类被认为具有特定特征的协议。

这与Runnable接口或Comparable接口相同,但在这里您实际上有一个覆盖(Serializable)的方法,而例如public void run()只是一个你给你班级的特质。

最后一个例子是Serializable实现TimerTask的事实。 RunnableTimerTask类一起使用来执行延迟或定期任务,因此时间任务也是可运行的,这样Timer就可以使用该方法启动任务。

编辑,因为您似乎对界面的用处感到困惑,您必须认为: Java是一种静态类型语言。这是什么意思?这意味着它需要在编译期间知道关于类型的所有内容,以便能够保证不会抛出运行时类型错误。

好的,现在假设Java API支持hipotetically类来绘制形状。因此,您可以为形状编写自己的类,然后将它们提供给此类(我们称之为Timer)。

ShapeDrawer需要知道如何绘制传递给它的形状,并且唯一方式确定它是确定每个ShapeDrawer对象必须具有名为Shape的方法,以便public void drawMe()可以在您附加到它的每个ShapeDrawer上调用此方法,而不会知道更多内容。

所以你声明了一个接口

Shape

这些类可以被视为public interface Shape { public void drawMe(); } 。如果某个班级是Shape,您可以将其传递给Shape班级,而不会有任何问题:

ShapeDrawer

因此,编译器很高兴,因为在添加形状时添加了class ShapeDrawer { public void addShape(Shape shape) { ... } public void drawShapes() { for (Shape s : shapes) s.drawMe(); } } 的类,您的类知道如何绘制这样的形状,开发人员很高兴,因为您将通用协议分开了来自他们特定实现的对象。

这是一种契约,如果你想要一个implements Shape类能够由Triangle绘制你必须声明那个方法,那么你可以调用例如

ShapeManager

答案 3 :(得分:1)

这与接口无关。相反,Thread位于java.lang包中,并且由于java.lang是默认包,因此您无需导入它。也就是说,默认情况下会导入java.lang。*,因此您无需自己显式导入它。

答案 4 :(得分:0)

您的代码编写方式无法编译。您声明了PrimeFinder implements Runnable,但它实际上并非@Override public void run()

至于为什么接口很有用,这是因为它们定义了无论实现如何都可以使用的类型。例如,如果您使用Closeable,则表示您可以close()。实际的类可以是实现它的任何一个(see full list),但是使用界面可以让你退后一步,只说它们都是Closeable

使用接口,您不会继承实现;你继承了一个TYPE。如果有另一个代码适用于Closeable类型(例如,它所做的就是通过异常检查对它们调用close()),那么您可以将implements Closeable传递给它。事实上,这个“请为我关闭这个东西,好的,谢谢”实用工具方法已经存在,并且它非常灵活,可以高度重复使用,因为它适用于界面。

通过使用接口而不是特定的实现类,您可以更清晰地封装逻辑。例如,使用Set接口的算法并不关心它实际上是TreeSet还是HashSet。缺乏依赖性是一件好事,因为它允许算法非常灵活 - 例如,它可以在将来使用SuperMagicalSet implements Set

使用接口还可以确保正确的封装,因为不知道实际的实现类是什么,您必须只使用接口提供的内容。

我建议阅读Josh Bloch的 Effective Java 2nd Edition

  • 第18项:首选接口为抽象类
  • 第19项:使用接口定义类型

答案 5 :(得分:0)

Thread类实现了Runnable。 Runnable对Thread没有任何了解,但是Thread知道Runnable的一切。

使用Thread的其他类知道Thread实现了Runnable中指定的接口。