Callable接口和Runnable接口之间的区别
一、源代码角度分析两接口间的区别
想学习好Future设计模式,我们应当首先将Callable、以及Runnable接口之间的区别弄明白:
不妨首先给出他们俩的源代码:
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
以及:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
由他们本身的接口定义我们就能够看出它们的区别:
- 如上面代码所示,callable的核心是call方法,允许返回值,runnable的核心是run方法,没有返回值
- call方法可以抛出异常,但是run方法不行
- 因为runnable是java1.1就有了,所以他不存在返回值,后期在java1.5进行了优化,就出现了callable,就有了返回值和抛异常
- callable和runnable都可以应用于executors。而thread类只支持runnable
它们的相同点:
- 两者都是接口
- 两者都需要调用Thread.start启动线程
二、从使用场景来分析两接口间区别:
2.1 Runnnable接口的使用场景
1)传递给线程对象执行任务;
2)作为线程池方法execute()
的入口参数来执行任务;
具体的实现又可以细分,具体如下面代码块所示:
/**
* @author Fisherman
*/
public class TheWayOfUsingRunnable {
public static void main(String[] args) {
//1)lambda表达式形式传递给线程构造器
Runnable runnable1 = () -> {
System.out.println("我是使用lambda表达式实现的Runnable对象实现 version1");
};
Thread thread1 = new Thread(runnable1);
thread1.start();
Thread thread1_1 = new Thread(() -> {
System.out.println("我是使用lambda表达式实现的Runnable对象实现 version2");
});
thread1_1.start();
//2)匿名内部类的方式实现Runnable对象的传入Thread构造器
new Thread(
new Runnable() {
@Override
public void run() {
System.out.println("我是使用匿名内部类的方式实现的Runnable对象实现");
}
}
).start();
//3)使用实现了Runnable接口的对象实例传入Thread构造器
RunnableTask runnableTask = new RunnableTask();
new Thread(runnableTask).start();
//4)Runnable类以及其子类作为线程任务提交给线程池,通过线程池维护的工作者线程来执行。
Runnable runnable2 = () -> {
System.out.println("我是使用lambda表达式实现的Runnable对象实现 不同的是:用于线程池的实现");
};
ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(runnable2);
executor.shutdown();
}
/**RunnableTask
* Runnable接口子类的实现类:RunnableTask
*/
private static class RunnableTask implements Runnable{
@Override
public void run() {
System.out.println("实现了Runnable接口的子类的对象实现");
}
//额外写一个方法:get
public String get(){
return "I am Fisherman.";
}
}
}
2.2 Callable接口的使用场景
callable对象实例可以作为线程池的submit()
方法入口参数
public class TheWayOfUsingCallable {
public static void main(String[] args) {
//callable对象实例可以作为线程池的submit()方法入口参数
ExecutorService executor = Executors.newCachedThreadPool();
IntegerCallableTask integerCallableTask = new IntegerCallableTask();
Future<Integer> future = executor.submit(integerCallableTask);
executor.shutdown();
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class IntegerCallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
Thread.sleep(1000);
for (int i = 0; i < 101; i++) {
sum += i;
}
return sum;
}
}
从上述代码块的执行结果来看,main线程会一直等到执行完call()
方法中的所有代码才会继续执行main线程中接下来的代码(等待发生在方法:future.get()
)。但是Runnable接口和Callable接口在线程池上的应用实际上是十分类似的。
但是单单比较Runnable接口和Callable接口的区别意义是不大的,而且也是不够全面的,因为关于比较两个接口的区别问题本身也是在我在学习Future设计模式中提出的问题,所以接下来会分析Future接口以及FutureTask类上Runnable接口和Callable接口的异同,这样一来我们可以对此有一个更深层次的认识;但是不妨,将此内容放在我的另外一篇博客中。