FutureTask的常见用法就是将耗时的任务做异步执行,而当前线程继续执行自己的任务,然后在需要的时候调用FutureTask的get()方法同步获取结果。具体代码如下:
package cn.cjc.ft;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
/**
* @author chenjc
* @since 2018-07-29
*/
public class FutureTaskTest {
public static void main(String[] args) throws Exception {
FutureTask<Integer> ft = new FutureTask<>(new MyTask());
// 异步执行
new Thread(ft).start();
// TODO: 当前线程继续处理其他任务
// ...
if (ft.isDone()) {
// 任务完成,可以直接拿到结果
Integer result = ft.get();
System.out.println(result);
} else {
// 任务没有完成,可以继续同步等待,或者取消任务
Integer result = ft.get(5, TimeUnit.SECONDS);
System.out.println(result);
// ft.cancel(true);
}
}
private static class MyTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// TODO: 执行耗时的任务
Thread.sleep(4000);
return Integer.MAX_VALUE;
}
}
}
在自己写程序的过程中,为了解决某个问题,在寻找中发现FutureTask还有一种用法,具体代码如下
package cn.cjc.ft;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author chenjc
* @since 2018-07-29
*/
public class FutureTaskTest2 {
/**
* 存储权限串和数字的映射关系,以节约内存
*/
private static final ConcurrentHashMap<String, Short> MAPPING_MAP = new ConcurrentHashMap<>();
private static final AtomicInteger SERIAL_NO = new AtomicInteger(0);
public static void main(String[] args) {
String permStr = "user:edit";
System.out.println(getSerialNo(permStr));
}
private static Short getSerialNo(String permissionString) {
Short key = MAPPING_MAP.get(permissionString);
if (key == null) {
// 这里可能会浪费一次计数
MAPPING_MAP.putIfAbsent(permissionString, (short) SERIAL_NO.incrementAndGet());
key = MAPPING_MAP.get(permissionString);
}
return key;
}
}
上面的这个程序主要功能是想把权限串映射成一个数字,后面用到这个权限串的地方,都用这个数字来代替,以达到节约内存的目的,但是这个程序有个弊端,那就是在多线程并发执行的情况下,如果相同的权限串没有被映射过,那么多线程并发映射的时候,就有可能会出现SERIAL_NO作一次无用的自增,如果系统的权限串足够多的话,SERIAL_NO就有可能被耗尽。
下面是通过FutureTask改进的映射
package cn.cjc.ft;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author chenjc
* @since 2018-07-29
*/
public class FutureTaskTest2 {
/**
* 存储权限串和数字的映射关系,以节约内存
*/
private static final ConcurrentHashMap<String, FutureTask<Short>> MAPPING_MAP = new ConcurrentHashMap<>();
private static final AtomicInteger SERIAL_NO = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
String permStr = "user:edit";
System.out.println(getSerialNo(permStr));
}
private static Short getSerialNo(String permissionString) throws Exception {
FutureTask<Short> ft = MAPPING_MAP.get(permissionString);
if (ft == null) {
ft = new FutureTask<>(new Callable<Short>() {
@Override
public Short call() throws Exception {
return (short) SERIAL_NO.incrementAndGet();
}
});
FutureTask<Short> ftExist = MAPPING_MAP.putIfAbsent(permissionString, ft);
if (ftExist == null) {
ft.run();
return ft.get();
} else {
return ftExist.get();
}
} else {
return ft.get();
}
}
}
可以看出,这样的话在并发的情况下就不会出现SERIAL_NO作无用自增了。