Java同步计数器不同线程打印相同的值

标签 java multithreading concurrency

我正在使用同步方法用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 类的五个不同实例:

 for (int i = 0; i < threadPoolSize; i++) {
    executorService.submit(new Counter());
 }

因此,有时五个线程可以同时执行 incrementCounter() 方法,因为它们锁定了 Counter 类的五个不同实例(当您使用 synchonized 方法,您实际上锁定了 this当前类实例)。

您可以创建一个新的静态对象,该对象必须同步,以便池中的所有线程都拥有并共享相同的公共(public)锁定对象:

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 > 原语使操作原子,即操作将在单个事务中发生,并且其他线程始终可以看到最新值。

关于Java同步计数器不同线程打印相同的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57347443/

相关文章:

java - 构建我的应用程序,但它说构建失败并出现一个错误

java - 在整数处找到一对字符串并相减,java

java - SQLite 减少 for 循环时间

Java JFace 数据绑定(bind) : Update SWT widget from background thread

java - 如何协调两个后台任务?

java - 私有(private)以太坊区 block 链中循环构造的 CompletableFuture

java - 超时队列会删除未检索到的对象

java - 如何在 UDP 协议(protocol)上同步通信?

java - Java 内存模型顺序一致性与 Leslie Lamport 定义有何不同?

kotlin - 在Kotlin中删除Iterator或List之前的任意内容