阿珍探出头看了看老徐的屏幕,全部都是绿色的曲线图,好奇地问:“老徐,你看的这是什么?”老徐看的太入神,转过头才发现阿珍,尬尴地笑了笑说:“我就是看看最近的行情。”老徐立马切换了窗口。
阿珍没在意又继续问到:“ Runnable 和 Callable 两个接口我总搞混,这个到底有什么不同?”
面对阿珍的灵魂拷问,老徐淡定自若地说:“ Runnable 是用于提供多线程任务支持的核心接口, Callable 是在Java 15中添加的 Runnable 的改进版本。”
“在聊它们不同之前,我们先分别了解一下两个接口。”老徐一边说着,一边打开了源码:
Runnable 接口是一个函数式接口,它只有一个run()方法,不接受任何参数,也不返回任何值。由于方法签名没有指定 throws 子句,因此无法进一步传播已检查的异常。它适用于我们不使用线程执行结果的情况,例如,异步打印日志:
在上面例中,根据 name 参数把信息记录在日志文件中,没有返回值。我们可以通过 Thread 启动,比如:
我们也可以通过 ExecutorService 启动,比如:
Callable接口也是一个函数式接口,它只有一个call()方法,不接受任何参数,返回一个泛型值V,在方法签名上包含 throws Exception 子句,因此我们可以很容易地进一步传播已检查异常。它适用于我们使用线程执行结果的情况,例如,异步计算阶乘:
在上面例中,根据 n 参数计算它的阶乘,并可以返回计算结结果。我们只能通过 ExecutorService 启动,比如:
call()方法的结果可以通过Future对象获取到,如果在调用Future对象的get()方法时,call()方法出现了异常,异常会被继续传递,比如:
抛出如下异常:
老徐回头看看了阿珍,说:“这回你知道有什么不同了吧!”阿珍一头雾水地说:“信息量有点大呀,可以给我总结一下吗?”“当然可以。”老徐回答。
Runnable和Callable的不同:
线程提交执行的时候就会被调用,就像run方法一样,只不过这里在未来可以得到call执行的结果
import javautilArrayList;import javautilconcurrentCallable;
import javautilconcurrentExecutionException;
import javautilconcurrentExecutorService;
import javautilconcurrentExecutors;
import javautilconcurrentFuture;
import javautilconcurrentTimeUnit;
class TaskWithResult implements Callable<String>{
private int id;
private static int count =10;
private final int time =count--;
public TaskWithResult(int id){
thisid = id;
}
@Override
public String call() throws Exception {
TimeUnitMILLISECONDSsleep(100);
return "Result of TaskWithResult : "+ id+", Time= "+time;
}
}
public class CallableDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService exec = ExecutorsnewCachedThreadPool();
ArrayList<Future<String>> results =new ArrayList<Future<String>>();
for(int i=0;i<10;i++){
resultsadd(execsubmit(new TaskWithResult(i)));
}
for(Future<String> fs : results){
Systemoutprintln(fsget());
}
}
}
创建线程的方式一:继承Thread类(由于Java单继承的特性,这种方式用的比较少)
步骤:
1、继承Thread类,然后重写run方法
2、创建子类对象,然后调用start()方法来启动线程
我们可以看到这边现在只创建了一个线程,那么如果要创建多个线程要怎么做呢?通过继承Thread的方式创建线程,想要创建多个不同的线程就要先创建多个不同的继承Thread的类,然后再根据上面的步骤1,2来创建线程,这显然有些麻烦,为了展示多线程,我们先在上面的线程中增加一个主线程,也就是main方法中执行的线程。如下:
创建线程的方式二:实现Runnable接口(Java可以实现多个接口,这种方式常用)
步骤:
1、创建一个类实现Runnable接口,然后重写run方法
2、创建实现类对象、代理类对象,然后代理类对象调用start()方法启动线程
用实现Runnable接口的方式,实现多线程:
《模拟抢票系统》,代码如下:
线程调用了start()方法,并不意味着立即执行,而是到就绪状态,等待cpu的调度,所以每次执行的结果都是不一样的。
创建线程的方式三:实现javautilconcurrent并发包下的Callable接口(进阶版,初学者做个了解)
步骤:
1、创建一个类实现Callable接口,然后重写call()方法
(和run方法不一样的是,call方法可以有返回值,并且可以抛出异常)
2、创建Callable的实现类对象--》创建执行服务--》提交执行服务得到Future对象--》获取结果--》停止服务
显示Mac在线就是在Mac上使用QQ呗
首先解释一下啥是Mac啊,Mac有多知种释意,但是这里的意思是Mac电脑,道也就是俗称的苹果电脑,就是说在苹果电脑上使用QQ就会显示Mac在线了
不过也不是绝对的,因为如果在普通的PC上安装黑苹果系统,或者使用虚拟机安装OS X(macOS)系内统容,在里面使用QQ的话也是可以显示Mac在线的
反过来也是一样的,如果你的Mac安装Windows系统,并且在Windows系统上运行QQ,也就变成普通在线了,或者虚拟机使用Windows系统,也是正常在线的~
FutureTask)在多线程上,如果没有要求你取线程的返回值,或者捕获异常,大家基本上使用的都是Thread或者Runnable,当面试时,被问到这个瞬间就是 what? 这是什么东西。
看过我上篇文章的伙伴应该知道Future,FutureTask《初级面试:如何向线程池提交任务,提交任务有几种方式有什么区别》没看过的莫慌。这篇文章我来告诉你他们是什么,有什么用。
大家都知道线程池有2中方式提交任务,分别是实现Runnable的类和Callable的类,从Runnable中的run方法中,也可以知道他没有返回值也没有抛出异常,这也就决定了它是实现不了具有返回值和抛异常的线程。如果我们需要获取返回值,就得想各种其他的办法来解决。自jdk15开始,提供了Callable,他们为我们提供了线程返回值和异常的功能。如图:

Callable
call方法抛出异常以及有返回值。(所以他与Runnable相比,优点就出来了),对比下如何实现线程。

Runnable实现

Callable实现
Runnable就不用说了,Callable必须要借助FutureTask封装才能启动线程,看过我上篇文章的伙伴肯定会说,我在线程池中并没有使用到FutureTask,而是直通过submit提交上去的。是因为submit里面也是使用了FutureTask,只是他帮我们写好了。如图:

submit
眼尖的人肯定看到了execute这个方法,我之前说过这个方法只有实现了Runnable的类才能使用,也就证明了FutureTask也是肯定实现了Runnable的。如图:

FutureTask
那么他除了包装Callable他还能干什么呢?

FutureTask方法
isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true;
isDone方法表示任务是否已经完成,若任务完成,则返回true;
cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false;参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true;
get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
从上面的源码中可以看见他实现了Future,而上面这些方法恰恰就是Future定义的。Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。也就是说Future提供了三种功能:1)判断任务是否完成;2)能够中断任务;3)能够获取任务执行结果。而FutureTask是Future的实现,FutureTask对象可以对实现了Callable和Runnable的对象进行包装,由于FutureTask也是实现了Runnable接口所以它可以提交给Executor来执行。
以上就是关于老徐和阿珍的故事:Runnable和Callable有什么不同全部的内容,包括:老徐和阿珍的故事:Runnable和Callable有什么不同、Java Callable接口的call方法什么时候被调用、如何创建线程如何保证线程安全等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)