Java方法有时被线程之一跳过

时间:2019-01-23 08:14:50

标签: java multithreading

在使用 2个线程的相同方法时遇到麻烦。

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

如果有任何专家会读这篇文章,请您描述一下发生的事情,如果是已知问题,请给我链接以进行解释。谢谢!

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());
  }
}

1 个答案:

答案 0 :(得分:5)

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

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

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

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

现在,假设有2个线程恰好在同一时间执行此序列,并且您了解了为什么该值不会增加。

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

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();
    }
}