【问题标题】:ExecutorService with AtomicInteger and Synchronized giving different outcomes具有 AtomicInteger 和 Synchronized 的 ExecutorService 给出不同的结果
【发布时间】:2020-02-02 10:43:32
【问题描述】:

以下是代码 - AtomicInteger

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class ExecutorExample1 {
    public static void main(String[] args) {
        ExecutorService executorService= Executors.newFixedThreadPool(2);
        executorService.execute(new MyTask());
        executorService.execute(new MyTask());
        executorService.execute(new MyTask());
        executorService.shutdown();
    }
}

class MyTask implements Runnable{
    private static AtomicInteger count = new AtomicInteger(0);
    @Override
    public void run() {
        try {
            count.addAndGet(1);
            task();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void task()throws InterruptedException{
        System.out.println(count + " Enterd Run of: " + Thread.currentThread().getName());
        Thread.sleep(5000);
        System.out.println(count + " Executing: " + Thread.currentThread().getName());
        Thread.sleep(5000);
        System.out.println(count + " Completed Executing: " + Thread.currentThread().getName());
    }
}

以上代码输出:

2 进入运行:pool-1-thread-1 2 进入运行:pool-1-thread-2 2 执行:pool-1-thread-2 2 执行:pool-1-thread-1 2 完成执行:pool-1-thread-1 2 完成执行:pool-1-thread-2 3 进入运行:pool-1-thread-1 3 执行:pool-1-thread-1 3 完成执行:pool-1-thread-1

用 int 和同步块替换 AtomicInteger 的相同代码

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class ExecutorExample1 {
    public static void main(String[] args) {
        ExecutorService executorService= Executors.newFixedThreadPool(2);
        executorService.execute(new MyTask());
        executorService.execute(new MyTask());
        executorService.execute(new MyTask());
        executorService.shutdown();
    }
}

class MyTask implements Runnable{
    //private static AtomicInteger count = new AtomicInteger(0);
    private static int count = 0;
    @Override
    public void run() {
        try {
            //count.addAndGet(1);
            synchronized (MyTask.class){
                count+=1;
            }
            task();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void task()throws InterruptedException{
        System.out.println(count + " Enterd Run of: " + Thread.currentThread().getName());
        Thread.sleep(5000);
        System.out.println(count + " Executing: " + Thread.currentThread().getName());
        Thread.sleep(5000);
        System.out.println(count + " Completed Executing: " + Thread.currentThread().getName());
    }
}

同步块代码的输出

2 进入运行:pool-1-thread-2 1 进入运行:pool-1-thread-1 2 执行:pool-1-thread-2 2 执行:pool-1-thread-1 2 完成执行:pool-1-thread-2 2 完成执行:pool-1-thread-1 3 进入运行:pool-1-thread-2 3 执行:pool-1-thread-2 3 完成执行:pool-1-thread-2

问题?

  1. 为什么输出会有差异?
  2. 为什么原子整数会增加到 2 而不是 1。
  3. 如何使用 atomicinteger 实现同步输出。
  4. 同时使用 volatile 和 atomic 有什么好处或用途?

【问题讨论】:

  • 期望是count维护线程执行run方法的计数。
  • 顺便说一句,在您的第二个示例中,您在同步块中增加变量,但是当您读取它时,您不会同步,这可能会产生可见性问题。

标签: java multithreading concurrency executorservice compare-and-swap


【解决方案1】:

AtomicInteger 在后台使用 volatile 字段。这样做是为了确保所有读者(其他线程)使用最新的值。在第二种情况下使用简单的int 时,您的字段不是volatile,因此您看到的1 来自一个陈旧的值。

通过使用volatile 关键字,您应该能够获得类似的结果。

另一种方法是使用门来确保满足条件,然后再继续前进。例如,这可以通过CountDownLatch 来实现(也有其他方法):https://www.baeldung.com/java-countdown-latch

【讨论】:

  • 使用 atomicinteger 为什么计数从 0 增加到 2 而不是 1?
  • 如果你运行它的次数足够多,你会发现它首先进入 1 的情况。当您首先看到 2 时,这仅表示两个线程在打印任何内容之前都执行了增量。
猜你喜欢
  • 1970-01-01
  • 2019-03-01
  • 2012-07-12
  • 2021-09-25
  • 2023-03-30
  • 1970-01-01
  • 2011-04-03
  • 2015-08-26
  • 2021-12-25
相关资源
最近更新 更多