关于java:Shall Callable比Runnable更受欢迎?

Shall Callable be preferred over Runnable?

在Java中,我理解了EDCOX1 0和EDCX1·1接口的区别。从Java 1.5向EDCOX1·0接口添加了额外的特性,并被称为EDCOX1×1,以保持向后兼容性。

我的问题是,现在我们有了Callable接口,我们应该一直使用它吗?不使用Callable和使用Runnable的用例是什么?

(这篇文章很好地说明了他们之间的区别)


两者都有各自的用途,并且都由java.util.concurrent中的Executor框架支持。runnable已经存在了更长的时间,但它仍然在使用中,并不气馁。

可调用的对象可以抛出异常并返回值,这使得它们更好地抽象了结果承载任务(例如从网络中获取资源、执行一个昂贵的计算值等)(在Java实践中,Goetz,布洛赫等人,Java并发的标准工作)。

因此,如果您正在设计API,我建议尽可能使用可调用文件。如果您确定任务不会返回值,也不会引发异常,那么runnables也是一个有效的选择。这里没有黑色和白色,特别是因为可运行文件可以很容易地用可调用文件包装,反之亦然。

另外,请注意,可调用的实现不需要声明throws Exception;事实上,可调用本身声明它只是允许实现者抛出任何选中的异常。但是,仅依赖可调用接口的可调用调用对象必须编写异常处理代码。还要注意,可调用项不需要返回值;您只需声明可调用项返回Void(大写'V)。


imho,runnable是一个更好的类型,当把一个函数作为参数使用时,

  • 没有返回值,但只有副作用
  • 必须处理异常,而不是传播它们

别忘了Callable.call()抛出异常。这意味着,如果您将一个可调用项作为参数,这个可调用项可以抛出任何类型的异常,并且您必须有一种正确的方法来处理它们。如果您不能做到这一点,最好让可调用的实现者按自己的意愿处理异常,并使参数成为可运行的,以使其明确。


不使用Callable的用例:ScheduledExecutorService.scheduleAtFixedRatescheduleWithFixedDelay只接受Runnable


我更喜欢Callables,但在那些罕见的情况下,您可能需要一个Callable作为Runnable,您可以通过向您的Callable中添加这样的run()方法轻松实现这两种方法。

1
2
3
4
5
6
7
public void run(){
    try{
        call();
    } catch (Exception e){
        e.printStackTrace(); // or however you log errors
    }
}

在Java 8中,你也可以为你做一个界面来做同样的事情:

1
2
3
4
5
6
7
8
9
public interface CallableRunnable<T> extends Callable<T>, Runnable {
    default void run(){
        try {
            call();
        } catch (Exception e) {
            e.printStackTrace(); // or however you log errors
        }
    }
}

然后你只要把implements Callable改成implements CallableRunnable就可以了,这样,你的工作就可以被任何需要的方法调用。当然,如果您需要特定的错误处理,您仍然可以重写run()方法来处理call()方法抛出的异常。您甚至可以实现一个方法来实现这一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface CallableRunnable<T> extends Callable<T>, Runnable {
    default void run(){
        try {
            call();
        } catch (Exception e) {
            handleCallExceptions(e);
        }
    }

    default void handleCallExceptions(Exception e){
        e.printStackTrace();
    }
}

那么任何特殊的异常处理只需要实现自己的handleExceptions(Exception)方法…但如果你不需要的话,你就不必这么做。我喜欢这样,因为它允许您使用日志框架等实现。


可调用和可运行两者都很相似,可以用于实现线程。在实现runnable的情况下,必须实现run()方法,但在可调用的情况下,必须实现call()方法,这两种方法的工作方式相似,但call()方法的灵活性更高,两者之间存在一定的差异。

可运行和可调用的区别如下--

1)runnable的run()方法返回void,意味着如果您希望线程返回一些可以进一步使用的东西,那么对于runnable run()方法您没有选择。有一个"可调用"的解决方案,如果您想以对象的形式返回任何东西,那么您应该使用可调用而不是可运行。可调用接口具有返回对象的方法"call()"。

方法签名-可运行的>

1
public void run(){}

可调用的>

1
public Object call(){}

2)对于runnable run()方法,如果出现任何选中的异常,则必须使用try catch块进行处理,但是对于callable call()方法,可以按如下方式引发选中的异常

1
 public Object call() throws Exception {}

3)RunnFabess来自遗留Java 1版本,但可调用的Java 1.5版本采用ExcUnter框架。

如果您熟悉执行器,那么应该使用Callable而不是Runnable。

希望你能理解。