与多个AtomicInteger增量相比,一个同步块

时间:2015-04-26 20:09:06

标签: java synchronized volatile compare-and-swap

我的理解是,最好使用AtomicInteger代替synchronized块来增加共享的 int 值。但是,如果多个int 值,它仍会保留吗?

以下哪种方法会更好,为什么?有没有更好的方法来提高性能?

1)使用synchronized块:

int i, j, k, l;
public void synchronized incrementValues() {
    i++;j++;k++;l++;
}

2)使用AtomicInteger:

AtomicInteger i,j,k,l;
// Initialize i, j, k, l

public void incrementValues() {
    i.incrementAndGet();
    j.incrementAndGet();
    k.incrementAndGet();
    l.incrementAndGet();
}

或者如果我使用ReentrantLock会更快吗?

3)使用ReentrantLock:

ReentrantLock lock = new ReentrantLock()
int i, j, k, l;
public void incrementValues() {
    lock.lock();
    try {
        i++;j++;k++;l++;
    } finally {
        lock.unlock();
    }
}

以下是我的问题:

  1. 3是最快的吗?
  2. 2怎么样?对于单个整数2,快于1.如果整数的数量增加,2会慢于1吗?
  3. 修改1 修改后的问题基于马蒂亚斯的回答。

    i,j,k,l彼此独立。个别增量应该是原子的,而不是整体的。如果线程2在线程1修改k之前修改i,则可以。

    编辑2 基于目前评论的附加信息

    我不是在寻找一个确切的答案,因为据我所知,这将取决于函数的使用方式和争用量等,并且测量每个用例是确定确切答案的最佳方法。但是,我希望看到人们分享他们的知识/文章等,这些将会影响影响情况的参数/优化。感谢@ Marco13这篇文章。它提供了丰富的信息。

1 个答案:

答案 0 :(得分:2)

首先,#2不是线程安全的。 incrementAndGet()是原子的,但是,连续调用四个incrementAndGet操作不是。 (例如,在第二个incrementAndGet之后,另一个线程可以进入相同的方法并开始执行相同的操作,如下例所示。

T1: i.incrementAndGet();
T1: j.incrementAndGet();
T1: k.incrementAndGet();
T2: i.incrementAndGet();
T2: j.incrementAndGet();
T1: l.incrementAndGet();
T2: k.incrementAndGet();
T2: l.incrementAndGet();

然后,如果它在#1和#3之间:如果你不进入高速股票交易,它对你来说不重要。可能存在很小的差异(在整数的情况下可能在几纳秒内),但它确实很重要。但是,我总是选择#1,因为它更简单,也更安全使用(例如,想象你会忘记将unlock()放在finally块中 - 那么你可能会遇到大麻烦)

关于您的修改: 对于数字1:有时一次原子地修改多个值可能很重要。考虑到数据不仅会增加,还会同时读取。您可以假设在任何时间点所有变量的值都非常相同。但是,由于更新操作在读取数据时不是原子操作,因此可能是I = j = k = 5且l = 4,因为执行增量的线程尚未到达上一个操作。 这是否是一个问题在很大程度上取决于你的问题。如果您不需要这样的保证,请不要关心。

对于2号: 优化很难,并发性更难。我只能建议不要考虑这种微观的oprimizations。在最好的情况下,这些优化可以节省纳秒,但会使代码变得非常复杂。在最坏的情况下,优化中存在错误假设或逻辑错误,您最终会遇到并发问题。但最有可能的是,您的优化会更糟糕。 还要考虑您编写的代码可能需要在以后的某个时间由其他人维护。在编程执行中节省了几毫秒的时间,你会浪费几个小时的处理器生命,试图理解你想要做什么以及为什么你这样做,同时试图修复那个令人讨厌的多线程bug。 所以为了方便起见:同步是最好的选择。

亲吻原则真的适用于并发。