一般创建线程只有两种方式,一种是继承Thread,一种是实现Runnable接口。但是这两种创建方式没有返回值,得使用共享变量或者其他通信方式才能得到线程处理完的结果。
一般不提倡使用继承Thread来创建线程方式,因为Java只有单继承,不能继承多个。但是Runnable是接口,所以使用Runnable可以在实现类的同时实现多个接口。
而且在线程池中,如果是使用Runnable来创建线程,那么可以直接将Runnable的实现类作为参数传入给线程池,使用线程池管理线程!
Java1.5之后有了Callable、Future,它们可以提供线程执行完的结果!
接下来简单介绍下Runnable、Callable 、Future、 FutureTask。
Runnable:
Runnable 是一个接口,在run方法中编写要执行的代码,但是没有任务返回接口,并且无法抛出异常。
public class Main {
static class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法正在执行...");
}
}
static class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
}
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread();
thread1.start();
Thread thread2 = new MyThread();
thread2.start();
System.out.println(Thread.currentThread().getName() + " main()方法执行结束");
}
}
Callable:
Callable也是一个接口,在call方法里编写要执行的代码,返回的是执行的结果。和Runnable相比,它有返回的结果,并且可以抛出异常!
使用Callable+FutureTask获取执行结果:
public class CallableTest {
static class MyCallable implements Callable<Integer> {
@Override
public Integer call() {
System.out.println(Thread.currentThread().getName() + " call()方法执行中...");
return 1;
}
}
public static void main(String[] args) {
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
try {
Thread.sleep(1000);
System.out.println("返回结果 " + futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " main()方法执行完成");
}
}
Future:
Future也是一个接口,它可以对具体的Runnable或者Callable任务进行取消、判断任务是否已取消、查询任务是否完成、获取任务结果。如果是Runnable的话返回的结果是null(下面会剖析为什么Runnable的任务,Future还能返回结果)。接口里面有以下几个方法。注意两个get方法都会阻塞当前调用get的线程,直到返回结果或者超时才会唤醒当前的线程。
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; //获取任务结果
}
使用Callable+Future获取执行结果:
public class CallableDemo implements Callable<Integer> {
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable子线程开始计算啦!");
Thread.sleep(2000);
for(int i=0 ;i<5000;i++){
sum=sum+i;
}
System.out.println("Callable子线程计算结束!");
return sum;
}
}
public class CallableTest {
public static void main(String[] args) {
//创建线程池
ExecutorService es = Executors.newSingleThreadExecutor();
//创建Callable对象任务
CallableDemo calTask=new CallableDemo();
//提交任务并获取执行结果
Future<Integer> future =es.submit(calTask);
//关闭线程池
es.shutdown();
try {
Thread.sleep(2000);
System.out.println("主线程在执行其他任务");
if(future.get()!=null){
//输出获取到的结果
System.out.println("future.get()-->"+future.get());
}else{
//输出获取到的结果
System.out.println("future.get()未获取到结果");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("主线程在执行完成");
}
}
FutureTask:
因为Future只是一个接口,所以是无法直接创建对象,因此就有了FutureTask。FutureTask不是接口了,是个class。它实现了RunnableFuture接口
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V> //RunnableFuture接口又继承了Runnable和Future
因此FutureTask可以作为Runnable被线程执行,又可以有Future的操作。它的两个构造器如下:
public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}
使用Callable+FutureTask获取执行结果的代码在Callable部分已介绍!