为什么计数同步不起作用



我希望最终计数始终为10000,但即使我在这里使用了synchronized,我也会得到1000以外的不同值。Java并发新手。

public class test1 {
static int count = 0;
public static void main(String[] args) throws InterruptedException {
int numThreads = 10;
Thread[] threads = new Thread[numThreads];
for(int i=0;i<numThreads;i++){
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
synchronized (this) {
for (int i = 0; i < 1000; i++) {
count++;
}
}
}
});
}
for(int i=0;i<numThreads;i++){
threads[i].start();
}
for (int i=0;i<numThreads;i++)
threads[i].join();
System.out.println(count);
}
}

Boris告诉你如何让你的程序打印正确的答案,但它打印正确答案的原因是,你的程序有效地是单个线程化的。

如果你执行了Boris的建议,那么你的run()方法可能看起来像这样:

public void run() {
synchronized (test1.class) {
for (int i = 0; i < 1000; i++) {
count++;
}
}
}

没有两个线程可以同时在同一对象上同步,并且程序中只有一个test1.class。这很好,因为也只有一个count。您总是希望锁定对象的数量及其生存期与它们应该保护的数据的数量和生存期相匹配。

问题是,您已经同步了run()方法的整个主体。也就是说,没有两个线程可以同时run()synchronized块确保它们都必须按顺序执行——就像您只是一个接一个地调用它们,而不是在单独的线程中运行它们一样。

这会更好:

public void run() {
for (int i = 0; i < 1000; i++) {
synchronized (test1.class) {
count++;
}
}
}

如果每个线程在每次增量操作后都释放了锁,那么这就给了其他线程并发运行的机会。


另一方面,所有的锁定和解锁都很昂贵。几乎可以肯定的是,多线程版本要比单线程程序花更长的时间才能达到10000。对此你无能为力。使用多个CPU来提高速度只有在每个CPU都可以独立完成的大型计算时才有效。

对于您的简单示例,您可以使用AtomicInteger而不是静态int并同步。

final AtomicInteger count = new AtomicInteger(0);

在Runnable内部只有这一行:

count.IncrementAndGet();

如果您在多线程代码环境中有更复杂的代码和许多函数要使用,那么使用同步块可以让其他线程使用整个类。

这段代码运行得并不快,因为将同一计数器1加1总是一个单一的操作,一次不能运行多次。

因此,如果你想把运行速度提高近10倍,你应该计算每个线程自己的计数器,而不是最后对结果求和。您可以使用执行器服务和可以为您返回结果的Future任务对ThreadPools执行此操作。

最新更新