FutureTask
FutureTask扩展了接口Runnable和Future,因此它可以作为Runnable用于Executor.submit(),也可以直接进行FutureTask.run()。FutureTask的构造函数可以使用Callable进行构造。FutureTask主要用于在多线程下,一个线程需要等待另外一个线程将某项任务执行完毕后才能继续执行。
编程模型
protected final ConcurrentHashMap<String, Future<String>> taskQueue =
new ConcurrentHashMap<>();
private String executeTask(final String taskName) {
while (true) {
Future<String> future = taskQueue.get(taskName); // 1.1和2.1 获取任务
if (future == null) {
Callable<String> task = new Callable<>() {
@Override
public String call() {
// 业务逻辑处理,此处经过处理后返回一个字符串
}
};
FutureTask<String> futureTask = new FutureTask<>(task); // 1.2创建任务
future = taskQueue.putIfAbsent(taskName, futureTask); // 1.3将任务入列
if (future == null) {
future = futureTask; // 1.4执行任务
futureTask.run();
}
}
try {
return future.get(); // 1.5和2.2 线程在此阻塞等待任务完成执行
} catch (InterruptedException ex) {
taskQueue.remove(taskName);
}
}
}
线程1和线程2并发执行上述代码,线程1从1.1处取出任务为空,执行代码直到1.4,开始执行任务,在1.5处阻塞等待,线程2在2.1处取出了同一名称的非空任务,在2.2处阻塞等待任务被完成执行。
实例
并发的多个线程不断从taskQueue中获取对应taskName的任务,假设线程Thread-A刚开始没有取得任务(即任务为空),于是创建新任务,并将其放入并发队列中,等待任务执行(FutureTask.run())完毕后,利用阻塞方法Future.get()获取结果。Thread-B执行相同名称的任务,因此在Thread-1执行到阻塞方法时,Thread-B从并发队列中获取了非空的任务,因而Thread-B也在Future.get()方法处等待任务执行完毕。实例中采用随机数方法获取最多10不同名称的taskName,因此必然有线程获取同样名称任务,确保了有线程在阻塞方法future.get()处等待。
package pool;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger;
public class FutureTaskTest {
private static ConcurrentHashMap<String, Future<String>> taskCache = new ConcurrentHashMap<String, Future<String>>();
private static final String TASK_NAME_PREFIX = "future_task_test";
private static void executeTask(final String taskName) {
Future<String> future = taskCache.get(taskName);
if (future == null) {
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
return taskName;
}
};
FutureTask<String> futureTask = new FutureTask<String>(callable);
future = taskCache.putIfAbsent(taskName, futureTask);
if (future == null) {
future = futureTask;
System.out.println(Thread.currentThread().getName() + ": run");
futureTask.run();
}
}
try {
Thread.sleep(1000);
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread().getName() + ": " + future.get());
} catch (InterruptedException | ExecutionException e) {
taskCache.remove(taskName);
}
}
public static void main(String[] args) {
int count = 0;
while (true) {
new Thread(new Runnable() {
@Override
public void run() {
executeTask(TASK_NAME_PREFIX + (int) (Math.random() * 10)); //确保任务名称流水号不超过10个
}
}, "Thread-" + (count++)).start();
// 控制线程数量
if (count > 50)
break;
}
}
}
测试结果如下,确实只有10个线程新建了任务,其余线程分别等待这10个线程执行完成任务后,继续执行。
time threadname type taskname
1537429412843 Thread-0: RUN future_task_test1
1537429412844 Thread-9: RUN future_task_test6
1537429412843 Thread-6: RUN future_task_test4
1537429412843 Thread-1: RUN future_task_test2
1537429412844 Thread-8: RUN future_task_test9
1537429412844 Thread-4: RUN future_task_test3
1537429412844 Thread-10: RUN future_task_test0
1537429412844 Thread-11: RUN future_task_test8
1537429412845 Thread-20: RUN future_task_test7
1537429412846 Thread-23: RUN future_task_test5
1537429413847 Thread-2: GET future_task_test1
1537429413847 Thread-6: GET future_task_test4
1537429413848 Thread-13: GET future_task_test1
1537429413848 Thread-19: GET future_task_test1
1537429413848 Thread-20: GET future_task_test7
1537429413849 Thread-25: GET future_task_test9
1537429413849 Thread-27: GET future_task_test4
1537429413849 Thread-5: GET future_task_test9
1537429413849 Thread-32: GET future_task_test9
1537429413849 Thread-36: GET future_task_test5
1537429413850 Thread-39: GET future_task_test3
1537429413850 Thread-42: GET future_task_test5
1537429413850 Thread-45: GET future_task_test6
1537429413850 Thread-41: GET future_task_test4
1537429413847 Thread-8: GET future_task_test9
1537429413847 Thread-1: GET future_task_test2
1537429413847 Thread-3: GET future_task_test9
1537429413847 Thread-9: GET future_task_test6
1537429413847 Thread-0: GET future_task_test1
1537429413850 Thread-38: GET future_task_test1
1537429413850 Thread-46: GET future_task_test9
1537429413848 Thread-18: GET future_task_test8
1537429413850 Thread-43: GET future_task_test7
1537429413850 Thread-44: GET future_task_test2
1537429413850 Thread-40: GET future_task_test7
1537429413850 Thread-12: GET future_task_test8
1537429413849 Thread-14: GET future_task_test4
1537429413849 Thread-37: GET future_task_test3
1537429413849 Thread-35: GET future_task_test7
1537429413849 Thread-31: GET future_task_test3
1537429413849 Thread-33: GET future_task_test6
1537429413849 Thread-34: GET future_task_test8
1537429413849 Thread-30: GET future_task_test5
1537429413849 Thread-7: GET future_task_test1
1537429413849 Thread-29: GET future_task_test4
1537429413849 Thread-28: GET future_task_test9
1537429413849 Thread-24: GET future_task_test5
1537429413849 Thread-26: GET future_task_test5
1537429413849 Thread-22: GET future_task_test1
1537429413849 Thread-23: GET future_task_test5
1537429413849 Thread-21: GET future_task_test2
1537429413848 Thread-16: GET future_task_test3
1537429413848 Thread-17: GET future_task_test0
1537429413848 Thread-11: GET future_task_test8
1537429413848 Thread-15: GET future_task_test2
1537429413847 Thread-4: GET future_task_test3
1537429413847 Thread-10: GET future_task_test0
1537429413854 Thread-48: GET future_task_test1
1537429413854 Thread-50: GET future_task_test2
1537429413854 Thread-49: GET future_task_test3
1537429413854 Thread-47: GET future_task_test1