FutureTask 和Future最直观的区别是:FutureTask 可以在new FutureTask<>(task)的时候就把线程加进去了最后再submit,Future必须先submit()然后再加入List
原因是:FutureTask 继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
实现runnable接口,说明可以把FutureTask实例传入到Thread中,在一个新的线程中执行。
实现Future接口,说明可以从FutureTask中通过get取到任务的返回结果,也可以取消任务执行(通过interreput中断)
import java.util.concurrent.Callable;
public class Task implements Callable {
private int n;
public int getN() {
return n;
}
public void setN(int n) {
this.n = n;
}
public Task(int n) {
this.n = n;
}
@Override
public Integer call() throws Exception {
Thread.sleep (1000);
int sum = 0;
for(int i=0;i
import java.util.concurrent.*;
/**
* @desc:
* @Author: Yongkang Hou
* @Date: 2019/2/22
*/
public class Test {
public static void main(String[] args) {
//第一种方式
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
FutureTask futureTask = new FutureTask(task);
executor.submit(futureTask);
executor.shutdown();
//第二种方式,注意这种方式和第一种方式效果是类似的,只不过一个使用的是ExecutorService,一个使用的是Thread
/*Task task = new Task();
FutureTask futureTask = new FutureTask(task);
Thread thread = new Thread(futureTask);
thread.start();*/
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("主线程在执行任务");
try {
System.out.println("task运行结果"+futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("所有任务执行完毕");
}
}
如果要同时运行多个子线程,并且得到结果汇总
错误代码实例:
public class Test {
public static void main(String[] args) {
long startMili=System.currentTimeMillis();
//启用5个线程
ExecutorService executor = Executors.newFixedThreadPool(5);
System.out.println("主线程在执行任务");
try {
int n=0;
for (int i = 0; i < 10; i++) {
FutureTask futureTask = new FutureTask(new Task(i));
executor.submit(futureTask);
n+=futureTask.get();
}
executor.shutdown();
System.out.println("task运行结果"+n);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
long endMili=System.currentTimeMillis();
System.out.println("所有任务执行完毕,用时"+(endMili-startMili));
}
}
运行结果
主线程在执行任务
pool-1-thread-1子线程在进行计算,计算结果0
pool-1-thread-2子线程在进行计算,计算结果0
pool-1-thread-3子线程在进行计算,计算结果1
pool-1-thread-4子线程在进行计算,计算结果3
pool-1-thread-5子线程在进行计算,计算结果6
pool-1-thread-1子线程在进行计算,计算结果10
pool-1-thread-2子线程在进行计算,计算结果15
pool-1-thread-3子线程在进行计算,计算结果21
pool-1-thread-4子线程在进行计算,计算结果28
pool-1-thread-5子线程在进行计算,计算结果36
task运行结果120
所有任务执行完毕,用时10051
用时10064显然是不对的,原因是futureTask.get();这一步会对当前线程堵塞直到拿到结果。
正确的写法应该是先启动线程,最后统一获取结果。
正确代码实例:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
/**
* @desc:
* @Author: Yongkang Hou
* @Date: 2019/2/22
*/
public class Test {
public static void main(String[] args) {
long startMili = System.currentTimeMillis ();
//启用5个线程
ExecutorService executor = Executors.newFixedThreadPool (5);
System.out.println ("主线程在执行任务");
try {
int n = 0;
List > futureTaskList = new ArrayList <> ();
for (int i = 0; i < 10; i++) {
FutureTask futureTask = new FutureTask <> (new Task (i));
executor.submit (futureTask);
futureTaskList.add (futureTask);
}
for (FutureTask integerFutureTask : futureTaskList) {
n += integerFutureTask.get ();
}
executor.shutdown ();
System.out.println ("task运行结果" + n);
} catch (Exception e) {
e.printStackTrace ();
}
long endMili = System.currentTimeMillis ();
System.out.println ("所有任务执行完毕,用时" + (endMili - startMili));
}
}
运行结果
主线程在执行任务
pool-1-thread-1子线程在进行计算,计算结果0
pool-1-thread-3子线程在进行计算,计算结果1
pool-1-thread-4子线程在进行计算,计算结果3
pool-1-thread-2子线程在进行计算,计算结果0
pool-1-thread-5子线程在进行计算,计算结果6
pool-1-thread-3子线程在进行计算,计算结果10
pool-1-thread-2子线程在进行计算,计算结果28
pool-1-thread-4子线程在进行计算,计算结果21
pool-1-thread-1子线程在进行计算,计算结果15
pool-1-thread-5子线程在进行计算,计算结果36
task运行结果120
所有任务执行完毕,用时2023
RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
如果异步的执行方法比较简单可以不用定义Task类,直接写内部方法
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test {
static ExecutorService pool = Executors.newFixedThreadPool (5);
public static void main(String[] args) {
Long startTime = System.currentTimeMillis ();
List list = new ArrayList <> ();
List > futureArrayList = new ArrayList <> ();
Future future;
//模拟学生数量
for (int i = 0; i < 30; i++) {
int age = i;
String name = "序号:" + i;
//批量给学生添加信息
future = pool.submit (() -> {
Thread.sleep (1000);
System.out.println ("线程:" + Thread.currentThread ().getName () + "正在执行");
Student student = new Student ();
student.setName (name);
student.setAge (age);
return student;
});
//此处千万不要直接get()(注释部分),因为future.get ()会对当前子线程堵塞直到获取结果,这样就会照成子线程运行完毕才会进行下一个子线程,失去多线程的效果
// Student student = future.get ();
// list.add (student);
// return list;
//将子线程结果预加入集合中,最后统一get(),这样就可以达到多线程同时执行的效果。
futureArrayList.add (future);
}
//关闭线程池
pool.shutdown ();
//把子线程结果赋给List
for (Future futures : futureArrayList) {
Student student = null;
try {
student = futures.get ();
} catch (Exception e) {
e.printStackTrace ();
}
list.add (student);
}
for (Student student : list) {
System.out.println (student.getName ());
System.out.println (student.getAge ());
System.out.println ("___________");
}
Long endTime = System.currentTimeMillis ();
System.out.println ("耗时:" + (endTime - startTime));
}
}