java的callable_Java中Runnable与Callable的那些事

8918f985823b

image

1.概述

自Java早期以来,多线程一直是该语言的一个主要方面。Runnable是用于表示多线程任务的核心接口,Callable是在Java 1.5中添加的Runnable的改进版本。

在本文中,我们将探讨两种接口的差异和应用。

2.执行机制

两个接口都旨在表示可由多个线程执行的任务。Runnable的任务都可以使用运行线程类或ExecutorService的,而可调用只能用后者来运行。

3.返回值

让我们深入了解这些接口处理返回值的方式。

3.1 使用Runnable

Runnable接口是一个功能接口,并且具有单一的run()不接受任何参数,并且不返回任何值的方法。

这适用于我们不查找线程执行结果的情况,例如,传入事件日志记录:

public interface Runnable {

public void run();

}

让我们通过一个例子来理解这个:

public class EventLoggingTask implements Runnable{

private Logger logger = LoggerFactory.getLogger(EventLoggingTask.class);

@Override

public void run() {

logger.info("Message");

}

}

在此示例中,线程将只读取队列中的消息并将其记录在日志文件中。任务没有返回任何值; 可以使用ExecutorService启动任务:

public void executeTask() {

executorService = Executors.newSingleThreadExecutor();

Future future = executorService.submit(new EventLoggingTask());

executorService.shutdown();

}

在这种情况下,Future对象不会保留任何值。

3.2 使用Callable

Callable接口是包含单一的通用call()调用方法-它返回一个通用值V:

public interface Callable {

V call() throws Exception;

}

我们来看看计算数字的阶乘:

public class FactorialTask implements Callable {

int number;

// standard constructors

public Integer call() throws InvalidParamaterException {

int fact = 1;

// ...

for(int count = number; count > 1; count--) {

fact = fact * count;

}

return fact;

}

}

call()方法的结果在Future对象中返回:

@Test

public void whenTaskSubmitted_ThenFutureResultObtained(){

FactorialTask task = new FactorialTask(5);

Future future = executorService.submit(task);

assertEquals(120, future.get().intValue());

}

4.异常处理

让我们看看它们对异常处理的适用程度。

4.1 使用Runnable

由于方法签名没有指定“throws”子句, 因此无法传播进一步异常的检查。

4.2 使用Callable

Callable的call()方法包含“throws Exception”子句,因此我们可以轻松地进一步传播已检查的异常:

public class FactorialTask implements Callable {

// ...

public Integer call() throws InvalidParamaterException {

if(number < 0) {

throw new InvalidParamaterException("Number should be positive");

}

// ...

}

}

如果你期望有返回值,那么这个情况下建议使用Callable,例外是在所收集的未来对象,这可以通过进行呼叫到被检查的Future.get()方法。这将抛出它包装的原始异常ExecutionException:

@Test(expected = ExecutionException.class)

public void whenException_ThenCallableThrowsIt() {

FactorialCallableTask task = new FactorialCallableTask(-5);

Future future = executorService.submit(task);

Integer result = future.get().intValue();

}

在上面的测试中,抛出ExecutionException,因为我们传递的是无效数字。我们可以在此异常对象上调用getCause()方法来获取原始的已检查异常。

如果我们不调用Future类的get()方法- 那么call()方法抛出的异常将不会被报告回来,并且该任务仍将被标记为已完成:

@Test

public void whenException_ThenCallableDoesntThrowsItIfGetIsNotCalled(){

FactorialCallableTask task = new FactorialCallableTask(-5);

Future future = executorService.submit(task);

assertEquals(false, future.isDone());

}

即使我们已将参数的负值抛出到FactorialCallableTask,上述测试也会成功通过。

5.结论

在本文中,我们探讨了Runnable和Callable接口之间的差异。

8918f985823b

image

微信关注:Java知己

每天更新Java知识哦,期待你的到来!

8918f985823b

image

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值