需要了解多线程环境下AtomicInteger代码使用中的问题

Need to understand the problem in AtomicInteger code usage in multithreaded environment

在一次面试中,有人问我一个编码问题,我必须找到该代码中的问题并提出适当的解决方案。

完整代码请见下方:

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

public class Atomic {

    static AtomicInteger count = new AtomicInteger(0);
    static int counter = 0;

    public static class Runnable extends Thread {

    public void run() {
        while (count.getAndSet(1) != 0) {
            try {
                Thread.sleep(3000);
            } catch (Exception e) {

            }
        }
        counter = counter + 1;
        count.set(0);
    }

}

public static void main(String[] args) {
    ExecutorService executor = Executors.newFixedThreadPool(10);
    for (int i = 0; i < 10; i++) {
        Runnable runnable = new Runnable();
        executor.execute(runnable);
    }
    executor.shutdown();
}

}

此代码 运行 正确。但问题是,如果线程数增加或者我 运行 For 循环将近 10000 次,这段代码就会出现问题。

我试图找出问题所在,但找不到。

这段代码有几处错误。你还没有用 "there is some problem" 表示,但这里有一些东西跳出来了。


首先,counter 变量未安全更新。多个线程不能保证最后写入的值的可见性;您也不能保证在读取和写入之间没有其他线程更新其值。

对此的简单解决方案:将 counter 更改为 AtomicInteger,然后使用 getAndIncrementincrementAndGet 递增。


其次,public static class Runnable extends Thread {非常可疑。

  1. 不隐藏大家熟知的名字Javaclasses(这是隐藏java.lang.Runnable
  2. 不要直接扩展 Thread,尤其是当您只需要 java.lang.Runnable 来添加带有 ExecutorService.
  3. 的执行时

更合适的 class 声明是:

public static class MyRunnable implements Runnable {

(或者随便你怎么称呼它)

或者你可以只声明一个匿名 class:

executor.execute(new Runnable() { /* body */ });

或者您可以只声明一个 lambda:

executor.execute(() -> { /* body */ });

第三,count 在这里似乎并没有真正起到明显的作用。 runnable的逻辑好像是:

  • 如果 "flag" 为假:
    1. 将 "flag" 设置为真
    2. 增加一个变量
    3. 将 "flag" 设置为 false
  • 否则:
    1. 等待 3 秒
    2. 再试一次

count在这里扮演着"flag"的角色。它实际上只是一个 AtomicBoolean.

但是你根本不需要单独的 count 变量,如果你将 counter 设为 AtomicInteger:

while (true) {
  int current = counter.get();
  if (counter.compareAndSet(current, current + 1)) {
    // Nothing else is trying to update "current" at the same time:
    // we updated it. Stop.
    break;
  }

  // Something else is trying to update at the same time.
  // Sleep for 3 seconds.
  Thread.sleep(3000);
}