每当我运行这个程序时,它会给我不同的结果。有人可以向我解释,或者给我一些我可以找到答案的主题,以便了解代码中会发生什么?
class IntCell {
private int n = 0;
public int getN() {return n;}
public void setN(int n) {this.n = n;}
}
public class Count extends Thread {
static IntCell n = new IntCell();
public void run() {
int temp;
for (int i = 0; i < 200000; i++) {
temp = n.getN();
n.setN(temp + 1);
}
}
public static void main(String[] args) {
Count p = new Count();
Count q = new Count();
p.start();
q.start();
try { p.join(); q.join(); }
catch (InterruptedException e) { }
System.out.println("The value of n is " + n.getN());
}
}
答案 0 :(得分:4)
原因很简单:您不会原子地获取和修改您的计数器,以致您的代码容易出现竞争条件问题。
以下是一个说明问题的示例:
n.getN()
获取0
n.getN()
获取0
n.setN(1)
将n
设置为1
n
设置为1
,因此仍然会调用n.setN(1)
将n
设置为1
而不是2
如您所料synchronized
,这称为竞争条件问题。您的最终结果将取决于执行代码时遇到的竞争条件问题的总量,这是不可预测的,因此它会从一个测试更改为另一个测试。
解决这个问题的一种方法是在IntCell
块中获取和设置你的计数器,以便在下一步中以原子方式执行它,实际上它会强制线程获取{{{}实例的独占锁。在能够执行此部分代码之前,已分配给n
。
synchronized (n) {
temp = n.getN();
n.setN(temp + 1);
}
<强>输出:强>
The value of n is 400000
您还可以考虑使用AtomicInteger
而不是int
作为您的计数器,以便依赖addAndGet(int delta)
或incrementAndGet()
类型的方法来原子递增计数器。
答案 1 :(得分:1)
对IntCell n
静态变量的访问是在两个线程之间并发:
static IntCell n = new IntCell();
public void run() {
int temp;
for (int i = 0; i < 200000; i++) {
temp = n.getN();
n.setN(temp + 1);
}
}
竞赛条件使得在执行n.setN(temp + 1);
时无法获得可预测的行为,因为它取决于之前调用的线程:temp = n.getN();
。
如果它是当前线程,则你有线程放置的值,否则你有另一个线程放置的最后一个值。
您可以添加同步机制以避免意外行为问题。
答案 2 :(得分:0)
您正在并行运行2个线程并通过这两个线程更新共享变量,这就是为什么您的答案总是不同的原因。像这样更新共享变量不是一个好习惯。
要理解,您应首先了解Multithreading然后notify and wait,simple cases
答案 3 :(得分:0)
使用两个并发线程修改相同的数字n。如果Thread1读取n = 2,则Thread2在写入增量之前读取n = 2,Thread1将n递增到3,但Thread2将不再递增,而是将另一个“3”写入n。如果Thread1在Thread2读取之前完成其递增,则两者都将递增。
现在两个线程都是并发的,你永远不知道哪一个会得到什么CPU周期。这取决于您机器上运行的其他内容。因此,通过上述覆盖情况,您将总是失去不同数量的增量。
要解决此问题,请通过n ++在n上运行实际递增。它们进入单个CPU周期。