请稍等 ...
×

采纳答案成功!

向帮助你的同学说点啥吧!感谢那些助人为乐的人

重试依然有问题

public class MayFail implements Computable<String, Integer>{
@Override
public Integer compute(String arg) throws Exception {
double random = Math.random();
if (random > 0.5) {
throw new IOException(Thread.currentThread().getName()+" 读取文件出错");
}
Thread.sleep(2000);
System.out.println(arg+“被加入缓存”);
return Integer.valueOf(arg);
}
}


public class ImoocCache9<A, V> implements Computable<A, V> {

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

private final Computable<A, V> c;

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

@Override
public V compute(A arg) throws InterruptedException, ExecutionException {
    while (true) {
        Future<V> f = cache.get(arg);
        if (f == null) {
            FutureTask<V> ft = new FutureTask<>(()->c.compute(arg));
            f = cache.putIfAbsent(arg, ft);
            if (f == null) {
                f = ft;
                System.out.println(Thread.currentThread().getName()+" 从FutureTask调用了计算函数");
                ft.run();
            }
        }
        try {
            return f.get();
        } catch (CancellationException e) {
            System.out.println("被取消了");
            cache.remove(arg);
            throw e;
        } catch (InterruptedException e) {
            cache.remove(arg);
            throw e;
        } catch (ExecutionException e) {
            System.out.println(Thread.currentThread().getName()+" "+e.getMessage()+" 计算错误,需要重试");
            cache.remove(arg);
        }
    }
}

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

}

打印:

Thread-2 从FutureTask调用了计算函数
Thread-1 从FutureTask调用了计算函数
Thread-2 java.io.IOException: Thread-2 读取文件出错 计算错误,需要重试
Thread-1 java.io.IOException: Thread-1 读取文件出错 计算错误,需要重试
Thread-0 java.io.IOException: Thread-1 读取文件出错 计算错误,需要重试
Thread-1 从FutureTask调用了计算函数
Thread-2 从FutureTask调用了计算函数
Thread-1 java.io.IOException: Thread-1 读取文件出错 计算错误,需要重试
Thread-0 从FutureTask调用了计算函数
Thread-1 从FutureTask调用了计算函数
Thread-2 java.io.IOException: Thread-2 读取文件出错 计算错误,需要重试
Thread-1 java.io.IOException: Thread-1 读取文件出错 计算错误,需要重试
Thread-0 java.io.IOException: Thread-0 读取文件出错 计算错误,需要重试
Thread-1 从FutureTask调用了计算函数
Thread-2 从FutureTask调用了计算函数
Thread-1 java.io.IOException: Thread-1 读取文件出错 计算错误,需要重试
Thread-0 从FutureTask调用了计算函数
Thread-1 从FutureTask调用了计算函数
Thread-2 java.io.IOException: Thread-2 读取文件出错 计算错误,需要重试
Thread-1 java.io.IOException: Thread-1 读取文件出错 计算错误,需要重试
Thread-0 java.io.IOException: Thread-0 读取文件出错 计算错误,需要重试
Thread-1 从FutureTask调用了计算函数
Thread-2 从FutureTask调用了计算函数
Thread-1 java.io.IOException: Thread-1 读取文件出错 计算错误,需要重试
Thread-0 从FutureTask调用了计算函数
Thread-1 从FutureTask调用了计算函数
Thread-0 java.io.IOException: Thread-0 读取文件出错 计算错误,需要重试
Thread-0 从FutureTask调用了计算函数

//重试导致缓存重复添加****************************************

667被加入缓存
666被加入缓存
666被加入缓存
第一次的计算结果:666
第三次的计算结果:667
第二次的计算结果:666

Process finished with exit code 0

老师,看打印结果,重试有时候会导致缓存重复添加,您的代码也是,运行多次也会出现这个问题,没想明白咋回事?

正在回答

1回答

这是因为,发生异常时,会有remove的操作:当前一个线程putIfAbsent放入值后,后一个线程此时remove,然后去putIfAbsent时会发现拿到的future是null,所以又会计算一次。

一种改进思路:执行remove时,如果发现之前已经有其他线程remove过,则此时不再remove。

1 回复 有任何疑惑可以回复我~
  • 提问者 灵森 #1
    搜嘎,阿里嘎多塔哒以马斯。
    回复 有任何疑惑可以回复我~ 2020-04-06 23:25:15
  • 提问者 灵森 #2
    老师,这个要怎么写鸭,加锁也没有用,有具体解决多次remove的办法么?
    回复 有任何疑惑可以回复我~ 2020-04-08 17:08:47
  • 提问者 灵森 #3
    老师,这个要怎么写鸭,加锁也没有用,有具体解决多次remove的办法么?
    回复 有任何疑惑可以回复我~ 2020-04-08 17:09:36
问题已解决,确定采纳
还有疑问,暂不采纳
意见反馈 帮助中心 APP下载
官方微信