Runnable和Callable接口解析
Runnable解析
Runnable是一个接口,它只有一个run()抽象方法,run()抽象方法需要被实现后才能使用.
1 | public interface Runnable { |
run()方法的返回值是void,故在执行完任务后无法返回任何结果
Thread类继承了Runnable接口,并且Thread中实现了run()方法.最后通过Thread类的start()方法就可以启动线程了.这是我们平时在应用中使用线程的常用方法
思考以下问题:
- 为什么调用start()方法就可以启动线程,而不是调用run().start()方法和决定线程运行内容的run()方法又有什么联系呢?
- 在Runnable层面实现run()方法和Thread层面实现run()方法的区别
- 怎么用Runnable实现run方法,并在Thread中执行Runnable实例化后重写的run方法
打开如下Runnable的源码,我们可以发现,它只含有一个抽象的run()方法.因为它是接口,所以需要被继承后重写run()方法才能使用
思考:
如果需要启动一个新的线程,这意味着需要分配给它CPU资源来执行这个线程,而CPU是不归JVM(Java虚拟机)直接管辖的,自然需要通过JVM通过外部的接口来实现和操作系统的对话,调整CPU资源的分配,所以,线程的创建一定是一个native方法(实现Java调用底层的C、C++代码).接口中的run()方法中的内容,仅仅代表线程运行的内容.一个结论出来了,Thread类中必然有native方法,Runnable无法脱离Thread类来新建线程.如果我们在new一个线程对象后直接调用run方法,也只是让当前线程去执行run()方法中的语句罢了,并没有实现多线程
在多线程的应用中我们更加倾向于使用Runnable来实现run()方法,因为这可以使得多线程在执行相同的代码的同时操作相同成员变量(多线程共享资源).因此,多线程的最佳打开方式是:写一个类继承Runnable接口,重写run()方法,再以Thread为媒介启动线程
Callable解析
Callable是java.util.concurrent包下的,也是一个接口,也只有一个call()方法,类似于java.lang.Runnable的run()方法,实现Callable接口的类和实现Runnable接口的类都是可以被其它线程执行的任务
可以看到call()方法是有返回值的,可以将执行的结果返回
1 | /** |
Callable和Runnable的区别:
- Callable中定义的是call()方法,Runnable中定义的是run()方法
- Callable中的call()方法可以返回执行任务后的结果,Runnable中的run()方法无法获得返回值
- Callable中的call()方法定义了throws Exception抛出异常,抛出的异常可以在主线程Future.get()时被主线程捕获;Runnable中的run()方法没有定义抛出异常,运行任务时发生异常时也会上抛,因为即使不加默认也会上抛RuntimeException,但异常无法被主线程获取
- 运行Callable任务可以拿到一个Future对象代表异步运算的结果