java - Java同步计数器相同的值由不同的线程打印
问题描述
我正在使用同步方法用 Java 编写同步计数器。代码如下
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class Counter implements Runnable {
private static int counter = 0;
private static final int limit = 1000;
private static final int threadPoolSize = 5;
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize);
for (int i = 0; i < threadPoolSize; i++) {
executorService.submit(new Counter());
}
executorService.shutdown();
}
@Override
public void run() {
incrementCounter();
}
private synchronized void incrementCounter() {
while (counter < limit) {
System.out.println(Thread.currentThread().getName() + " : " + counter);
counter++;
}
}
}
代码工作正常。但在某些时候,两个线程打印相同的数字,
pool-1-thread-2 : 29
pool-1-thread-2 : 30
pool-1-thread-1 : 30
pool-1-thread-1 : 32
如上面的输出,线程 2 和 1 都在打印30
。我不知道为什么会这样。任何帮助表示赞赏
解决方案
当您使用一个synchronized
方法时,执行该方法的线程将获得Counter
该类的当前实例的锁,因此其他线程可以同时增加该counter
值而不受任何抑制。
这是因为您Counter
在循环中创建了五个不同的类实例:
for (int i = 0; i < threadPoolSize; i++) {
executorService.submit(new Counter());
}
因此,有时五个线程可以同时执行该incrementCounter()
方法,因为它们正在锁定 Counter 类的五个不同实例(当您使用一个synchonized
方法时,您实际上是锁定在this
当前类实例上)。
您可以创建一个必须启用的新static
锁对象,synchronized
以便池中的所有线程都拥有并共享相同的公共锁对象:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
class Counter implements Runnable {
private static AtomicInteger counter = new AtomicInteger(0);
private static final Object lock = new Object();
private static final int limit = 1000;
private static final int threadPoolSize = 5;
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize);
for (int i = 0; i < threadPoolSize; i++) {
executorService.submit(new Counter());
}
executorService.shutdown();
}
@Override
public void run() {
incrementCounter();
}
private void incrementCounter() {
synchronized (lock) {
while (counter.get() < limit) {
System.out.println(Thread.currentThread().getName() + " : " + counter.get());
counter.incrementAndGet();
}
}
}
}
此外,由于这是您正在执行读取-更新-写入操作的情况,您应该考虑使用AtomicInteger
代替int
原语来使操作原子化,即操作将在单个事务中发生,并且其他线程始终可以看到最新值.
推荐阅读
- keras - lstm对ucf-101的评价方法
- java - 值未存储在 HashMap 中
- angular - how to get data from API to Angular Mat editable table using Form builder?
- c# - NullReferenceException 从表单转换为 WPF
- c++ - 尝试将 std::find 与自定义类型对象的向量一起使用(C2679,二进制'==':无运算符,rhs,'const _Ty'),Visual Studio 15.9.25
- javascript - 如何将ajax数据分配给@foreach(@foreach($loans as $loan))循环到Laravel的同一视图中
- vue.js - 使用 Vue Axios (CORS) 发送/存储会话 cookie 时出现问题
- excel - Application.CalculateUntilAsyncQueriesDone 崩溃 Excel
- python - 由于 Keras 和 Tensorflow 中的版本不兼容,如何更改“层”结构
- python - 删除 xticks 并减少观察 matplotlib 误差条之间的空间