首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >清单5.18中的Java并发性为什么不能用锁自动完成呢?

清单5.18中的Java并发性为什么不能用锁自动完成呢?
EN

Stack Overflow用户
提问于 2013-11-21 08:13:19
回答 1查看 220关注 0票数 7

在Java并发的实践中,在第106页上,它说“Memoizer3容易受到两个线程看到null并启动昂贵计算的问题的影响,因为复合操作(put-if- expensive )是在后台映射上执行的,而使用锁定无法使其成为原子操作。”我不明白为什么他们说它不能用锁来原子化。以下是原始代码:

代码语言:javascript
运行
复制
package net.jcip.examples;

import java.util.*;
import java.util.concurrent.*;

/**
 * Memoizer3
 * <p/>
 * Memoizing wrapper using FutureTask
 *
 * @author Brian Goetz and Tim Peierls
 */
public class Memoizer3 <A, V> implements Computable<A, V> {
    private final Map<A, Future<V>> cache
        = new ConcurrentHashMap<A, Future<V>>();
    private final Computable<A, V> c;

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

    public V compute(final A arg) throws InterruptedException {
        Future<V> f = cache.get(arg);
        if (f == null) {
            Callable<V> eval = new Callable<V>() {
                public V call() throws InterruptedException {
                    return c.compute(arg);
                }
            };
            FutureTask<V> ft = new FutureTask<V>(eval);
            f = ft;
            cache.put(arg, ft);
            ft.run(); // call to c.compute happens here
        }
        try {
            return f.get();
        } catch (ExecutionException e) {
            throw LaunderThrowable.launderThrowable(e.getCause());
        }
    }
}

为什么像这样的东西不能工作呢?

代码语言:javascript
运行
复制
...
public V compute(final A arg) throws InterruptedException {
    Future<V> f = null;
    FutureTask<V> ft = null;
    synchronized(this){
        f = cache.get(arg);
        if (f == null) {
            Callable<V> eval = new Callable<V>() {
                public V call() throws InterruptedException {
                    return c.compute(arg);
                }
             };
             ft = new FutureTask<V>(eval);
             f = ft;
             cache.put(arg, ft);                 
        }
    }
    if (f==ft) ft.run(); // call to c.compute happens here
    ...
EN

回答 1

Stack Overflow用户

发布于 2013-11-22 22:20:20

当然,它可以通过使用锁来原子化,想象一下最原始的情况:你在整个函数周围有一个全局锁,那么一切都是单线程的,因此是线程安全的。我假设他们要么是其他意思,要么是普遍的误解。

您的代码甚至可以通过使用ConcurrentHashMap的putIfAbsent方法进行改进,如下所示:

代码语言:javascript
运行
复制
public V compute(final A arg) throws InterruptedException {
  Future<V> f = cache.get(arg);
  if (f == null) {
    final Callable<V> eval = new Callable<V>() {
      public V call() throws InterruptedException {
        return c.compute(arg);
      }
    };
    final FutureTask<V> ft = new FutureTask<V>(eval);
    final Future<V> previousF = cache.putIfAbsent(arg, ft);
    if (previousF == null) {
      f = ft;
      ft.run(); 
    } else {
      f = previousF; // someone else will do the compute
    } 
  }
  return f.get();
}

最后,f要么是之前添加的值,要么是初始值,代价是额外创建一个Callable,但对计算的调用不会超过一次。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20109897

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档