【发布时间】:2016-07-08 07:03:38
【问题描述】:
编辑: 我已经在栈上找到了答案: https://stackoverflow.com/a/16280842/3319557
我遇到了同步问题。我有以下两种方法:
public synchronized void incrementCounter1() {
counter++;
}
public void incrementCounter2() {
synchronized (counter) {
counter++;
}
}
我在许多线程中(分别)测试每一个。第一种方法的行为符合预期,但第二种 (incrementCounter2) 是错误的。有人可以解释为什么会这样吗?
我认为这个方法设计得很好,因为我在 Java Concurrency in Practice 中发现了一些类似的东西。摘自本书:
@ThreadSafe
public class ListHelper<E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
...
public boolean putIfAbsent(E x) {
synchronized (list) {
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
}
}
我使用我正在修改的对象中的监视器,就像书中一样。
完整代码在这里:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SynchronizationTest {
public static final int N_THREADS = 500;
public static final int N_Loops = 5000;
private Integer counter = 0;
Lock l = new ReentrantLock();
public void incrementCounter0() {
counter++;
}
public synchronized void incrementCounter1() {
counter++;
}
public void incrementCounter2() {
synchronized (counter) {
counter++;
}
}
public void incrementCounter3() {
try {
l.lock();
counter++;
} finally {
l.unlock();
}
}
private interface IncrementStrategy {
void use(SynchronizationTest t);
}
private static class IncrementingRunnable implements Runnable {
SynchronizationTest synchronizationTest;
IncrementStrategy methodToUse;
public IncrementingRunnable(SynchronizationTest synchronizationTest, IncrementStrategy methodToUse) {
this.synchronizationTest = synchronizationTest;
this.methodToUse = methodToUse;
}
@Override
public void run() {
for (int i = 0; i < N_Loops; i++) {
methodToUse.use(synchronizationTest);
}
}
}
public void test(IncrementStrategy methodToUse, String methodName) {
counter = 0;
Thread[] threads = new Thread[N_THREADS];
for (int i = 0; i < N_THREADS; i++) {
threads[i] = new Thread(new IncrementingRunnable(this, methodToUse));
threads[i].start();
}
for (int i = 0; i < N_THREADS; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(methodName + " diff than expected " + (counter - N_THREADS * N_Loops));
}
public void test() {
test(t -> t.incrementCounter0(), "incrementCounter0 (expected to be wrong)");
test(t -> t.incrementCounter1(), "incrementCounter1");
test(t -> t.incrementCounter2(), "incrementCounter2");
test(t -> t.incrementCounter3(), "incrementCounter3");
}
public static void main(String[] args) {
new SynchronizationTest().test();
}
}
我知道,应该使用ExecutorService,整个问题可以用AtomicLong解决,但这不是这个问题的重点。
代码的输出是:
incrementCounter0 (expected to be wrong) diff than expected -1831489
incrementCounter1 diff than expected 0
incrementCounter2 diff than expected -599314
incrementCounter3 diff than expected 0
PS。 如果我将该字段添加到 SynchronizationTest
Object counterLock = new Object();
并改变 incrementCounter2 到:
public void incrementCounter2() {
synchronized (counterLock) {
counter++;
}
}
然后 incremetCounter2 按预期工作。
【问题讨论】:
-
虽然这是一个合适的问题,但我是否建议您提供一个简短示例,说明您认为什么“不起作用”以及预期和实际输出。如果这个问题应该帮助其他有同样问题的人,那就必须更简洁
-
我用示例输出粘贴了我的代码。预期是 incrementCounter2 与预期 -599314 的差异将有 0 而不是随机值
-
信息太多了。对于这样的问题,首先要了解真正的问题是什么。
标签: java concurrency synchronization synchronized