当前位置: 首页 > 面试题库 >

在实践中的Java并发中,Brian Goetz

商昆琦
2023-03-14
问题内容

Brian Goetz的Java Concurrency in Practice提供了一个有效的可伸缩缓存供并发使用的示例。这是该类的代码:

public class Memoizer<A, V> implements Computable<A, V> {
    private final ConcurrentMap<A, Future<V>> cache
        = new ConcurrentHashMap<A, Future<V>>();
    private final Computable<A, V> c;

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

    public V compute(final A arg) throws InterruptedException {
        while (true) {
            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 = cache.putIfAbsent(arg, ft);
                if (f == null) { f = ft; ft.run(); }
            }
            try {
                return f.get();
            } catch (CancellationException e) {
                cache.remove(arg, f);
            } catch (ExecutionException e) {
                throw launderThrowable(e.getCause());
            }
        }
    } }

可能是个愚蠢的问题,但有人能告诉我该类的并发用法吗?喜欢在主?

干杯,Agata


问题答案:

这是一个计算阶乘的示例:

public static void main(String[] args) throws Exception {

    //create a memoizer that performs factorials
    final Memoizer<Integer, Integer> memo = new Memoizer<Integer, Integer> (new Computable<Integer, Integer>() {
        @Override
        public Integer compute(Integer a) {
            int result = 1 ;
            for(int i = 1 ; i < a ; i++){
                result = result*i;
            }
            return result;
        }
    });

    //now call the memoizer
    System.out.println(memo.compute(10));


    //call it with 10 threads concurrently
    ExecutorService exec = Executors.newFixedThreadPool(10);
    ExecutorCompletionService<Integer> compService = new ExecutorCompletionService<Integer>(exec);
    for(int i = 0 ; i < 15 ; i++){
        compService.submit(new Callable<Integer>(){
            @Override
            public Integer call() throws Exception {
                return memo.compute(5);
            }
        });
    }
    exec.shutdown();
    for(int i = 0 ; i < 15 ; i++){
        System.out.println(compService.take().get());
    }
}

因此,如果两个线程尝试在完全相同的时间计算相同的阶乘,则由于putIfAbsent线程安全,因此只有其中一个将实际执行计算。第二个线程将简单地获取第一个线程在地图中放置的future,并等待其完成。



 类似资料:
  • 问题内容: 我对上面的示例有一个疑问,该示例是从 练习 清单14.12计数使用锁实现的信号量的 Java并发中 提取的。 我想知道为什么我们需要在构造函数中获取锁(如所示的lock.lock()被调用)。据我所知,构造函数是 原子的 (引用转义除外),因为没有其他线程可以获取该引用,因此,其他线程看不到一半构造的对象。因此,我们不需要构造函数的同步修饰符。此外,只要对象被安全发布,我们也不必担心

  • 问题内容: 在《 Java Concurrency In Practice》中,作者指出 不变对象可以通过任何机制发布 不可变对象可以在没有附加同步的情况下被任何线程安全地使用,即使不使用同步来发布它们。 这是否意味着以下成语可以发布不可变对象? 会有数据竞赛吗?(这意味着线程B可能无法在线程A添加的列表中看到不可变对象) 非常感谢你。 此外,作者说,如果Resource是不可变的,以下代码是安全

  • null 对于此问题的第二部分,将在另一个问题(单击此处)中详细讨论

  • 书中解释了在上面的代码中...“因为Widget和LoggingWidget中的doSomething方法都是同步的,所以在继续之前,每个方法都试图获取小部件上的锁。” 我运行了上面的代码来观察内部锁。上面的引文似乎暗示线程在Widget对象上获得了一个内在锁,但我观察到的是线程在LoggingWidget上获得了一个锁。我不知道如何核实收购数量,所以无法观察到这一点。 这本书是可以互换地使用lo

  • 当为一个特殊的任务设计并发代码时,需要根据任务本身来考虑之前所提到的问题。为了展示以上的注意事项是如何应用的,我们将看一下在C++标准库中三个标准函数的并行实现。当你遇到问题时,这里的例子可以作为很好的参照。在有较大的并发任务进行辅助下,我们也将实现一些函数。 我主要演示这些实现使用的技术,不过可能这些技术并不是最先进的;更多优秀的实现可以更好的利用硬件并发,不过这些实现可能需要到与并行算法相关的

  • 我对上面的示例有一个问题,该示例是从Java并发实践清单14.12使用锁实现的计数信号量中提取的。 我想知道为什么我们需要获取构造函数中的锁(如图所示lock.lock()被调用)。据我所知,构造函数是原子的(除了引用转义),因为没有其他线程可以获得引用,因此,半构造对象对其他线程不可见。因此,我们不需要构造函数的同步修饰符。此外,我们也不需要担心内存可见性,只要对象安全发布。 那么,为什么我们需