Java多线程(6)-Runnable与Callable

Runnable 与 Callable是什么?

在前面的文章叙述中,有一句很明确的表达,java中只有Thread对象代表一个线程对象,

一个线程像一个工人一样,按照我们提供的说明书一句一句的读,并按照说明书说的去做,

RunnableCallable 其实就是一份说明书。

线程的几种创建方式

1.

new Thread() {
  @Override 
  public void run() {
    try {
      Thread.sleep(60000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}.start();

2.

new Thread(() -> {
  try {
    Thread.sleep(60000);
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}).start();

当我们开启了两个线程以后,可以通过idea里的调试器看到,分别有一个Thread-0Thread-1 的线程,

Java多线程(6)-Runnable与Callable_第1张图片

这两个线程其实就是我们刚刚new出来的两个线程,为什么我们new出来的线程名字是Thread-0|1 ?

image.png

通过构造函数可以看到,jvm帮我们将线程名称默认以Thread-开头加上一个数字,作为默认的名称。

通过观察Thread-0这个线程,我们可以看到该线程的最底层方法是Thread.run,事实上所有开启的新线程,方法栈的底部一定都是Thread.run

Java多线程(6)-Runnable与Callable_第2张图片

通过查看run方法的源码,我们可以看到,该方法中,判断target(Runnable类型),是否存在,如果存在则执行targetrun方法,

Java多线程(6)-Runnable与Callable_第3张图片

target是一个Runnable接口,接口在java中是一种规约,是一个可以被执行的东西,那么这个target是从哪里来的呢?

我们看一下刚刚创建线程的代码,

Java多线程(6)-Runnable与Callable_第4张图片

查看该构造函数,可以看到这个构造函数接收的参数就是一个Runnable ,

image.png

也就是说其实这个Runnable是我们构建的一个Runnable的匿名接口实现类,这个实现类其实就是一份代码说明书, new Thread是新招来的一个工人,

我们把新工人招来后,给了他一份说明书,让他对着说明书去执行任务。

到这里我们可以总结一下,Runnable其实就是一个任务说明,可以被任意一个线程进行执行,这是他们的区别,是任务线程的区别, Runnable是用来被Thread执行的。

Callable

Runnable已经说完了,那Callable是做什么的 ?

Runnable有一个弊端,因为它的run方法是没有返回值的,但是如果我们有一个需求,当一个任务执行完成之后,希望拿到它的返回值呢?

Java多线程(6)-Runnable与Callable_第5张图片

当然通过其他的方式,我们也许可以拿到Runnable的返回值,例如定义一个全局的Map或者其他类,当这个Runnable执行结束的时候,调用一下这个Map或者其他的类,用于保存结果,但是这种方式很不优雅,

这是Runnable的限制之一,

同时因为接口的限制,Runnable的实现类,不可以抛出checked exception, 因为该方法定义时,没有声明将可能抛出的异常,所以该接口的实现也不可以抛出,只能在实现内部自己消化, 这是限制之二,

下面可以看下Callable的源码,可以看到有一个call方法,同事带有返回值,并且允许抛出异常,

Java多线程(6)-Runnable与Callable_第6张图片

RunnableCallable的作用相同,都是一个任务,只是二者一个有返回值一个没有,一个可以抛出异常另一个不可以,

他们都属于被Thread执行的类,这是他们三者之间的关系和区别,这两个接口在线程池中会有很大的作用,后续再继续讲解。

你可能感兴趣的:(Java)