Java方法有时会被其中一个线程跳过

bod*_*ich 2 java multithreading

我在两个线程使用相同的方法时遇到了麻烦.

问题是:看起来当一个线程调用方法,并且方法正在运行,同时另一个线程尝试调用相同的方法时,Java只是跳过该调用!因此,当2个线程调用相同的方法时,该方法只被调用一次.因此,有时候随机编程只是跳过方法,代码根本不执行!怎么可能?在不同的机器上尝试,它的工作绝对不像预期,对我来说看起来像一个可怕的Java bug.

如果有任何专家会阅读本文,请说明发生了什么,如果已知问题,请给我解释的链接.谢谢!

PS:代码完全正常,你甚至可以将它粘贴到像repl.it或类似的在线java.在a.updateCounter(inc)之后; 我检查增量器是否工作(第45行).

class Main {
  public static void main(String[] args) {
    ThreadOne t1 = new ThreadOne(1000, 300, "Thread 1");
    ThreadOne t2 = new ThreadOne(1, 300, "Thread 2");
    Thread one = new Thread(t1);
    Thread two = new Thread(t2);
    one.start();
    two.start();
  }
}

class Accum {
  private static Accum a = new Accum();
  private int counter = 0;
  private Accum() {}
  public static Accum getAccum() {
    return a;
  }

  public void updateCounter(int add) {
    counter += add;
  }

  public int getCount() {
    return counter;
  }
}

class ThreadOne implements Runnable {
  Accum a = Accum.getAccum();
  int inc;
  int max;
  String threadName;

  ThreadOne(int i, int c, String name) {
    inc = i;
    max = c;
    threadName = name;
  }

  public void run() {
    for(int x = 0; x < max; x++) {
      int check = a.getCount();
      a.updateCounter(inc);
      if(check + inc != a.getCount()) System.out.println("\nupdateCounter was skipped at x " + x + "\nThread name: " + threadName);
      try {
        Thread.sleep(5);
      } catch(InterruptedException ex) {
        System.out.println("x: " + x + "\n ex: " + ex);
      }
    }
    System.out.println(threadName + ": " + a.getCount());
  }
}
Run Code Online (Sandbox Code Playgroud)

Ben*_*oit 5

首先,Java不会跳过调用.这只是与您的测试不符的实际结果.

int check = a.getCount();和测试之间if(check + inc != a.getCount()),计数器只是由另一个线程改变.就这么简单.

更新:操作counter += add不是ATOMIC,即您可以将其视为3个操作的序列:

  • counter读取的值
  • 计算新值
  • 此新值已分配给 counter

现在想象两个线程在同一时间完全执行此序列,并且您理解为什么值不会增加.

使这个调用成为原子的解决方案:只需使用AtomicInteger@gudok提到的:

import java.util.concurrent.atomic.AtomicInteger;

public class Accum {
    private static Accum a = new Accum();
    private AtomicInteger counter = new AtomicInteger(0);
    private Accum() {}
    public static Accum getAccum() {
        return a;
    }

    public void updateCounter(int add) {
        counter.addAndGet(add);
    }

    public int getCount() {
        return counter.intValue();
    }
}
Run Code Online (Sandbox Code Playgroud)