2

在Java中,为什么在下面的代码中synchronized可以注释掉?因为加法几乎是原子的,因此错误或失败的概率太小?

public class AddInParallel {

    public static void main(String[] args) {

        class sumValue {
            public int sum = 0;
            sumValue() {}
            public /*synchronized*/ void add(int i) {
                sum += i;
            }
        }

        // final makes sumValue visible to threads
        final sumValue sum = new sumValue();

        class Adder implements Runnable {

            // used to distinguish threads
            private int id;

            Adder(int index) { id = index; }

            public void run() { sum.add(1); }

        }    

        // start threads
        Thread[] threads = new Thread[1000];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new Adder(i) {});
            threads[i].start();
        }

        // wait for threads above to finish
        for (int i = 0; i < threads.length; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(sum.sum);
    }
}
4

3 回答 3

0

正如 Sotirios 所建议的,如果我们在这里更改代码:

public /*synchronized*/ void add(int i) {
    for (int j = 0; j < 1000; j++) {
        sum += i;
    }
}

最终通常不会是一百万。所以需要同步。谢谢。

于 2013-03-31T15:26:41.617 回答
0

这种陈述不能总是被认为是原子的:

sum += i; 

实际上,这涉及三个步骤(计算临时变量以存储 的当前值sum)。

在这种情况下,即使变量声明volatile上的关键字sum也不相关(因为赋值是基于其当前值)。

因此,您应该取消注释synchronized关键字以启用锁定,以免在您的流程中遇到“意外”=> 意味着总是以1000.

于 2013-03-31T15:27:18.063 回答
0

synchronized关键字不应被注释掉。每当您从多个线程写入变量时,(至少)写入本身应该受到某种同步的保护——synchronized关键字、ReentrantReadWriteLock等。正如您在问题中提到的那样,原因是不能保证单个写入是原子的。

虽然我怀疑这段代码的目的是为了证明一个观点,但同样值得注意的是,你不需要为这段代码使用锁。AtomicInteger您可以通过使用变量而不是变量int来获得正确的解决方案,而无需使用任何类型的同步sum

具体来说,您可以替换public int sumpublic AtomicInteger sum=new AtomicInteger();,而正文为add()

public void add(int i) {
    sum.addAndGet(i);
}

...你现在有了一个无锁的、正确的实现。(你必须在其他几个地方更新代码才能打印和运行,但我会把它作为练习留给读者。:)

于 2013-03-31T15:29:41.577 回答