第五天 Java中的线程Runnable,Thread,Callable
我们知道在java中我们现实线程有三种途径
1:Runnable这个是个接口,这个类只有一个方法public abstract void run();
2:Thread这个是个类,而且这个类本身就实现了Runnable
3:Callable这个也是一个接口,并且只定义了一个方法V call() throws Exception;这个是有返回值的方法,所以要和Future一起使用,Future定义了两个主要的方法V get() throws InterruptedException, ExecutionException;和V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
Thread
我们先来看看这个线程类,为什么我们要先看 Thread那,因为不过是 Runnable还是Callable这两个接口只有一个方法,在不使用线程池的情况下就算我们实现了这两个接口但是我们的线程还是跑不起来的,所以我们要使用Thread来承载他们的运行,
Thread代码里面我们能看到Thread的初始化方法很多,但是接受线程参数只能是Runnable类型的,
我们知道Thread线程要运行只有一个start方法
在start方法里面有一个 start0()方法这个方式是native的因为要多线程的话 需要底层系统支持,这个方法是c写的方法
private native void start0();
调用了这个方法start0() cup会另起线程调用public void run(),就是这个方法
这里target就是我们初始化的时候传递的Runnable类型的任务了,这样就是多线程运行了我们的任务
Runnable
这个是一个接口,里面只有一个方法 run()方法,没有什么可说的,实现起来也很简单直接继承这个接口就可以啦 ,但是我们前面说了 ,Runable只有run方法,是不能多线程的,因为他没有启动多线程的start方法,所以需要Thread的start来承载,【当然我们也可以直接调用Runnable的run方法,那样就不是多线程了,而是同步调用】
运行步骤很简单:开启一个Thread然后把我们Runnable类型的任务传递进去,通过start调用系统底层另起线程调用我们重写的run
Callable
我们知道Runnable和Thread都是没有返回值的,如果我们异步多线程要获取返回值怎么办,那就是Callable,这个接口定义了一个 call返回返回线程执行的结果
但是我们知道不管是Runnable还是Callable都是不能直接运行的应为没有start方法,Runnable可以通过Thread来运行,但是Callable怎么运行那 ,毕竟Thread只能接受Runnable类型的任务,这就需要我们的FutureTask了 ,这个FutureTask是继承了Runnable的一个实现类
继承关系是:FutureTask--》RunnableFuture--》1:Runnable 2:Future
Future接口:
这两个get是我们获取Callable返回值的关键
而且 FutureTask因为继承了Runnable所有可以被Thread承载使用start来启动执行,这样一个有返回值的异步线程算是闭环了,下面我们看一下具体的操作
这是一个简单的例子
我们创建一个 FutureTask然后放到了Thread里面去运行那么FutureTask是一个什么那
我们看到FutureTask有两个初始化方法,
1:如果传递的是Callable那直接把任务设置给this.callable然后设置线程状态是new【新线程】
2:如果是Runnable类型的任务那就需要转化然后在设置给this.callable然后设置线程状态是new【新线程】而且注意Runable的初始化可以设置一个 result作为返回值
因为Runable本身run是没有返回值的
Executors.callable(runnable, result);是怎么吧Runable转化为Callable的那其实很简单就是调用一个 适配器
上面我们说过Thread是运行线程是通过start调用系统底层另起线程然后调用run来执行的 ,FutureTask继承了Runable那肯定也重写了run方法,因为这个是异步线程要调用的,我们看看他做了什么

操作步骤:
1:拿到我们初始化的任务Callable类型的【Runnable类型的会被转化为Callable类型的】
2:判断线程状态,只有是New新线程才能执行
3:调用Callable的call方法【如果是Runable转为为Callable的话会在call中在调用run】,执行完call获取返回值赋值给result
4:执行set方法,吧返回值保存起来【因为FututeTask继承的是Runable,run方法是没有返回值的,要想异步拿到返回值需要把返回值做处理】
5:在set()中,判断线程状态是不是完成了,如果是完成了就把返回值赋值给outcome返回执行更新线程状态,如果不是完成状态不处理
这样线程执行的过程就完成了,但是我们执行完了还是没有拿到返回值呀 ,所以我们需要调用FutureTask的get方法来拿到返回值futureTask.get()
get方法分为了两个 ,一个是有过期时间的,一个是没有过期时间的
我们先来看一下有过期时间的

get步骤也不复杂
第一步:判断线程状态是不是完成状态,如果不是就等待【这里需要注意,如果我们FutureTask提交的任务很耗时,在没有执行完之前调用了get那一样会堵塞主线程的】
第二步:线程执行完了,那就通过report来拿到返回值
第三步:report里面在判断线程状态,然后拿到我们在上面set()时候设置的那个outcome返回出去
有过期什时间的get其实和没有过期差不多,就是多了个时间判断
第一步:判断线程状态,如果线程是没有完成,那去堵上过期时间awaitDone方法就是堵塞过期时间,使用LockSupport信号量来做的,如果过期时间到了线程还没有完成,那返回的线程状态还是小于COMPLETING,然后get就会抛异常,2:过期时间内线程完成了返回的线程状态大于COMPLETING 那么get就会返回report拿到返回值
这就是有返回值的异步线程的执行

浙公网安备 33010602011771号