FutureTask的用法

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 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 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> 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 ft = MAPPING_MAP.get(permissionString);
        if (ft == null) {
            ft = new FutureTask<>(new Callable() {
                @Override
                public Short call() throws Exception {
                    return (short) SERIAL_NO.incrementAndGet();
                }
            });
            FutureTask ftExist = MAPPING_MAP.putIfAbsent(permissionString, ft);
            if (ftExist == null) {
                ft.run();
                return ft.get();
            } else {
                return ftExist.get();
            }
        } else {
            return ft.get();
        }
    }
}

可以看出,这样的话在并发的情况下就不会出现SERIAL_NO作无用自增了。

你可能感兴趣的:(Java基础)