首页
学习
活动
专区
圈层
工具
发布

Java并发
EN

Stack Overflow用户
提问于 2014-05-06 14:18:50
回答 6查看 285关注 0票数 1

我试图在多线程环境中实现某种积累逻辑;我想知道在没有锁和同步关键字的情况下是否有更好/更快的方法来实现它?以下是我的当前代码:

代码语言:javascript
复制
public class ConcurrentHashMapTest {

    private static final int NB_THREADS = 1_000;

    private final Map<String, Integer> cnts = new HashMap<>();

    private static final Lock RWLOCK = new ReentrantLock(true);

    private static final String[] NAMES = {
        "A", "B"
    };

    public void testIt() {
        ExecutorService executor =
            Executors.newFixedThreadPool(NB_THREADS);
        for (int i = 0; i < NB_THREADS; i++) {
            Runnable task = new WorkerThread();
            executor.submit(task);
        }
        executor.shutdown();
        try {
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println(cnts);
    }

    private void accumulate(String name) {
        RWLOCK.lock();
        try {
            Integer cnt = cnts.get(name);
            if (cnt == null) {
                cnts.put(name, 1);
            } else {
                cnts.put(name, cnt + 1);
            }
        } finally {
            RWLOCK.unlock();
        }
    }

    private class WorkerThread implements Runnable {
        @Override
        public void run() {
            accumulate(NAMES[ThreadLocalRandom.current().nextInt(0, NAMES.length)]);
        }
    }
} 
EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2014-05-06 14:44:37

Java 8:

代码语言:javascript
复制
private final Map<String, AtomicInteger> cnts =
        new ConcurrentHashMap<>();

private void accumulate(String name) {
    cnts.computeIfAbsent(name, k -> new AtomicInteger()).incrementAndGet();
}

可以从多个线程自由访问ConcurrentHashMap。如果键不在映射中,则computeIfAbsent方法使用lambda进行计算以获得键的值,并在没有此类映射的情况下将其添加,然后返回该值。它实际上是putIfAbsent,其次是get。该值是一个值为0的新AtomicInteger。是否存在一个现有值,或者一个值为0的新值是否刚刚被添加,在任何一种情况下都会增加它。

Java 7:

代码语言:javascript
复制
private final ConcurrentMap<String, AtomicInteger> cnts =
        new ConcurrentHashMap<>();

private void accumulate(String name) {
    cnts.putIfAbsent(name, new AtomicInteger());
    cnts.get(name).incrementAndGet();
}

对于Java7,没有computeIfAbsent方法,但实际上只执行一个putIfAbsent和一个get,所以通过调用这些方法实现了同样的效果。不需要担心映射中已经存在该值;只有当映射没有该键的值时,才会添加一个新的零AtomicInteger。即使在我们前面有另一个线程并添加了一个零,这两个线程也会看到并增加相同的AtomicInteger实例。

票数 3
EN

Stack Overflow用户

发布于 2014-05-06 14:25:30

使用带有String和AtomicInteger的并发散列映射。两者都是线程安全的,因此可以自由使用。

票数 1
EN

Stack Overflow用户

发布于 2014-05-06 14:35:45

在这种情况下,我会谨慎地在ReentrantLock上使用公平性,因为如果等待更长时间的线程首先获得访问,对累加器没有好处。看看Brian的“Java并发在实践中”

为什么我们不想让所有的锁都公平?毕竟,公平是好的,不公平是坏的,对吗?(这不是偶然的,每当孩子们想上诉一个决定,“这是不公平的”几乎肯定会出现。我们认为公平是非常重要的,他们知道这一点。在现实中,对锁的公平保证是非常强大的,而且需要付出很大的性能代价。确保公平性所需的簿记和同步意味着争用的公平锁的吞吐量将比不公平锁低得多。默认情况下,您应该将fair设置为false,除非对您的算法的正确性至关重要,即线程的服务顺序与它们排队的顺序完全一致。

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

https://stackoverflow.com/questions/23497337

复制
相关文章

相似问题

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