人物背景:
老徐,男,本名徐福贵,从事Java相关研发工作多年,职场老油条,摸鱼小能手,虽然岁数不大但长的比较着急,人称老徐。据说之前炒某币败光了所有家产,甚至现在还有欠债。
阿珍,女,本名陈家珍,刚刚入职不久的实习生,虽然是职场菜鸟但聪明好学。据说是学校的四大校花之一,追求她的人从旺角排到了铜锣湾,不过至今还单身。
阿珍探出头看了看老徐的屏幕,全部都是绿色的曲线图,好奇地问:“老徐,你看的这是什么?”老徐看的太入神,转过头才发现阿珍,尬尴地笑了笑说:“我就是看看最近的行情。”老徐立马切换了窗口。
阿珍没在意又继续问到:“Runnable
和Callable
两个接口我总搞混,这个到底有什么不同?”
面对阿珍的灵魂拷问,老徐淡定自若地说:“Runnable
是用于提供多线程任务支持的核心接口,Callable
是在Java 1.5中添加的Runnable
的改进版本。”
“在聊它们不同之前,我们先分别了解一下两个接口。”老徐一边说着,一边打开了源码:
@FunctionalInterface public interface Runnable { public abstract void run(); }
Runnable
接口是一个函数式接口,它只有一个run()方法,不接受任何参数,也不返回任何值。由于方法签名没有指定throws
子句,因此无法进一步传播已检查的异常。它适用于我们不使用线程执行结果的情况,例如,异步打印日志:
package one.more; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LoggingTask implements Runnable { private static Logger logger = LoggerFactory.getLogger(LoggingTask.class); private String name; public LoggingTask(String name) { this.name = name; } @Override public void run() { logger.info("{}说:你好!", this.name); } }
在上面例中,根据name
参数把信息记录在日志文件中,没有返回值。我们可以通过Thread
启动,比如:
public static void main(String[] args) { String name = "万猫学社"; Thread thread = new Thread(new LoggingTask(name)); thread.start();; }
我们也可以通过ExecutorService
启动,比如:
public static void main(String[] args) { String name = "万猫学社"; ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.execute(new LoggingTask(name)); executorService.shutdown(); }
@FunctionalInterface public interface Callable<V> { V call() throws Exception; }
Callable接口也是一个函数式接口,它只有一个call()方法,不接受任何参数,返回一个泛型值V,在方法签名上包含throws Exception
子句,因此我们可以很容易地进一步传播已检查异常。它适用于我们使用线程执行结果的情况,例如,异步计算阶乘:
package one.more; import java.util.concurrent.Callable; public class FactorialTask implements Callable<Integer> { private int n; public FactorialTask(int n) { this.n = n; } @Override public Integer call() throws IllegalArgumentException { int fact = 1; if (n < 0) { throw new IllegalArgumentException("必须大于等于零"); } for (int i = n; i > 1; i--) { fact = fact * i; } return fact; } }
在上面例中,根据n
参数计算它的阶乘,并可以返回计算结结果。我们只能通过ExecutorService
启动,比如:
public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newSingleThreadExecutor(); Future<Integer> future = executorService.submit(new FactorialTask(5)); System.out.println(future.get()); executorService.shutdown(); }
call()方法的结果可以通过Future对象获取到,如果在调用Future对象的get()方法时,call()方法出现了异常,异常会被继续传递,比如:
public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newSingleThreadExecutor(); Future<Integer> future = executorService.submit(new FactorialTask(-1)); System.out.println(future.get()); executorService.shutdown(); }
抛出如下异常:
老徐回头看看了阿珍,说:“这回你知道有什么不同了吧!”阿珍一头雾水地说:“信息量有点大呀,可以给我总结一下吗?”“当然可以。”老徐回答。
Runnable和Callable的不同:
ExecutorService
启动,Runnable可以通过Thread
和ExecutorService
启动。最后,谢谢你这么帅,还给我点赞、推荐和关注。