一、Java线程实现基础
java中实现一个线程的方法是继承(extends)Thread
类或者实现(implements)Runnable
接口(我个人认为通过线程池启线程也算是)。
继承Thread
类:
class A extends Thread{
@Override
public void run() {
//TODO
}
}
实现Runnable
接口:
class B implements Runnable{
@Override
public void run() {
//TODO
}
}
抑或分别使用匿名内部类,如下:
new Thread(){
public void run() {
//TODO 操作
};
}.start();
new Thread(
new Runnable() {
@Override
public void run() {
//TODO 操作
}
}
).start();
其实Thread
类自身就实现了Runnable
接口:
public class Thread implements Runnable{
}
二、Callable和Runnable
Callable
和Runnable
接口申明定义分别如下:
/**
* A task that returns a result and may throw an exception.
* Implementors define a single method with no arguments called
* {@code call}.
*
* <p>The {@code Callable} interface is similar to {@link
* java.lang.Runnable}, in that both are designed for classes whose
* instances are potentially executed by another thread. A
* {@code Runnable}, however, does not return a result and cannot
* throw a checked exception.
*
* <p>The {@link Executors} class contains utility methods to
* convert from other common forms to {@code Callable} classes.
*
*/
@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;
}
public interface Runnable {
public abstract void run();
}
它们都只有一个方法。相对而言,我们对Runnable
接口还是比较熟悉。如Callable
接口注释所说,我们知道Callable
和Runnable
类似,都是为其实例可能由另一个线程执行而设计。但是Callable
接口是泛型形式,call()
方法,有返回值且可抛出异常,即可返回线程执行结果。
三、Future和FutureTask
3.1、Future介绍:
Future
可以对具体的Callable
任务进行相关操作:取消/中断当前任务、查询任务是否完成、获取任务结果。其中get()方法会阻塞,直到任务完成返回结果,下一篇介绍其实现原理。Future声明如下(详细请见JDK文档):
public interface Future<V> {//泛型接口
/**
* 取消/中断当前任务
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
*是否已取消/中断当前任务
*/
boolean isCancelled();
/**
* 查询任务是否完成
*/
boolean isDone();
/**
* 获取任务结果(会阻塞)
*/
V get() throws InterruptedException, ExecutionException;
/**
* 获取任务结果(会阻塞)
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
在Future
接口说明处已给出了伪代码来说明如何使用:
interface ArchiveSearcher { String search(String target); }
class App {
ExecutorService executor = ...
ArchiveSearcher searcher = ...
void showSearch(final String target)
throws InterruptedException {
Future<String> future
= executor.submit(new Callable<String>() {
public String call() {
return searcher.search(target);// 异步线程处理搜索
}});
displayOtherThings(); // 在异步搜索的同时,在当前线程中,我们同时干别的事情,爽哉
try {
displayText(future.get()); // 通过Future获取异步任务结果
} catch (ExecutionException ex) { cleanup(); return; }
}
}}
3.2、FutureTask介绍:
在Future
接口说明处同时也提到其具体实现类FutureTask
。先看一下如下类图:
可见FutureTask
则是一个RunnableFuture
,而RunnableFuture
则既继承了Runnbale
又继承了Futrue
:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
public class FutureTask<V> implements RunnableFuture<V>{}
另,查看源码发现其构造函数依赖注入Runnbale
或Callable
,且Runnable
会被Executors.callable()转换为Callable
类型,即FutureTask
最终都是执行Callable
类型式任务:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
/**当不需要返回值时,result可以传null*/
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);//被Executors.callable()转换为`Callable`
this.state = NEW; // ensure visibility of callable
}
Executors.callable()相关代码如下:
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
static final class RunnableAdapter<T> implements Callable<T> {//适配器模式
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
综上,可见FutureTask
间接实现了Runnable
,因此它既可以通过Thread
来直接执行,又可以提交给ExecuteService
来执行(并且还可以直接通过get()函数获取得到执行结果),上述Future
示例伪代码部分可以改造如下:
FutureTask<String> future =
new FutureTask<String>(new Callable<String>() {
public String call() {
return searcher.search(target);
}});
executor.execute(future);
或
FutureTask<String> future =
new FutureTask<String>(new Callable<String>() {
public String call() {
return searcher.search(target);
}});
new Thread(future).start();//没使用线程池,只是示意可以这么用