main函数里用lambda表达式创建任务,会输出3次“从FutureTask调用了计算函数”?

来源:13-5 用Future解决重复计算问题

小曼巴0

2021-12-29

package imoocCache;

import imoocCache.computable.Computable;
import imoocCache.computable.ExpensiveFunction;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

/**
 * ImoocCache2缺点:如果在计算完成前,另一个要求计算相同值的请求到来,会导致计算两遍,这和缓存想避免多次计算的初衷恰恰相反
 * 改进:利用Future避免重复计算
 */
public class ImoocCache3<A,V> implements Computable<A,V> {

    private final Map<A, Future<V>> cache = new ConcurrentHashMap<>();
    private  final Computable<A,V> c;

    public ImoocCache3(Computable<A, V> c) {
        this.c = c;
    }

    @Override
    public V compute(A arg) throws Exception {
        Future<V> f = cache.get(arg);
        if (f == null) {
            Callable<V> callable = new Callable<V>() {
                @Override
                public V call() throws Exception {
                    return c.compute(arg);
                }
            };
            FutureTask<V> ft = new FutureTask<>(callable);
            f = ft;
            // 任务开始计算之前先存入缓存,这样其他计算相同任务的线程就可以直接从缓存中读取该任务的执行结果
            // 如果该任务还没执行完成,则其他线程调用Future.get方法时会被阻塞
            cache.put(arg, ft);
            System.out.println("从FutureTask调用了计算函数");
            ft.run();
        }
        return f.get();  // get方法在任务计算过程中会被阻塞,直到任务完成
    }

    public static void main(String[] args) throws Exception {
        ImoocCache3<String, Integer> expensiveComputer = new ImoocCache3<>(
                new ExpensiveFunction());
        new Thread(() -> {
            try {
                Integer result = expensiveComputer.compute("666");
                System.out.println("第一次的计算结果:"+result);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                Integer result = expensiveComputer.compute("666");
                System.out.println("第三次的计算结果:"+result);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(()-> {
            try {
                Integer result = expensiveComputer.compute("667");
                System.out.println("第二次的计算结果:"+result);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }
}

写回答

1回答

小曼巴0

提问者

2021-12-29

使用lambda表达式时,线程2在线程1执行cache.put之前就已经进入了if语句内部,而创建Runnable对象则不会?

难道是因为创建Runnable需要消耗一定的时间,在这段时间内线程1已经执行完cache.put语句?而使用lambda表达式不用创建Runnable对象,线程1和线程2几乎同时执行?

下载视频          
0
1
悟空
这个我试过了,不是lambda表达式的问题,如果用Runnable对象同样可能有重复计算问题,因为大家是同一时间去做的compute,而且667用不上666的缓存。你试一下在第3次计算前,sleep一下,就会发现,不会重复计算了。
2021-12-30
共1条回复

深度解密Java并发工具,精通JUC,成为并发多面手

JUC全方位讲解,构建并发工具类知识体系

1599 学习 · 573 问题

查看课程