为什么即使使用 AtomicInteger 来跟踪进度,此代码也不是线程安全的?

Why is this code not thread-safe even though an AtomicInteger is used to track progress?

我尝试制作一个 class 来扩展线程,它只接受一个字符串数组并交替打印前 2 个字符串,进行 10000 次迭代。我使用 AtomicInteger(计数器)跟踪要打印的索引,但输出有时会打印: 你好 你好 你好 w 你好 你好 等等 而不是在每次迭代中交替。为什么会这样,如果不将 'synchronized' 放入 运行 方法中,我该如何解决它?

public class MyThreadDelegate implements Runnable {

  List<String> words;
  AtomicInteger counter = new AtomicInteger(0);

  public MyThread(List<String> words) {
    this.words = words;
  }

  @Override
  public void run() {
    for (int i = 0; i < 10000; i++) {
      System.out.println(words.get(counter.getAndIncrement()%2) + counter.get());
    }
  }

  public static void main(String[] args) {

    MyThreadDelegate myThreadDelegate = new MyThreadDelegate(Arrays.asList("hello", "w"));

    Thread t1 = new Thread(MyThreadDelegate);
    Thread t2 = new Thread(MyThreadDelegate);

    t1.start();
    t2.start();
  }
}

虽然号码是一个一个检索的,但方法的其余部分并未同步。所以有时可能会发生这种情况:

  • t1: 从计数器中获取值 0
  • t2: 从计数器中获取值 1
  • t2:打印 w
  • t1:打印 hello

一个快速解决方法是将整个 System.out 行放在一个 synchronized 块中,但这并不能保证线程轮流执行。它只是保证在下一个之前检索、递增和打印 echt 值。

如果您想让线程真正轮流执行,则必须实施某种锁定。但是如果你不想让线程互相等待,为什么要使用多线程?

编辑:另外,如果您打算以这种方式使用它,您可能应该让 MyThread 实现 Runnable 而不是扩展 Thread。有关更多信息,请参见 link:https://www.baeldung.com/java-runnable-vs-extending-thread(Solomon Slow 击败了我 :)