繁体   English   中英

Java线程问题?

[英]Java threading issue?

我想知道为什么结果不是40万。有两个线程为什么会被阻止?

class IntCell {
    private int n = 0;
    public int getN() {return n;}
    public void setN(int n) {this.n = n;}
}
class Count extends Thread {
    private static IntCell n = new IntCell();
    @Override 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());
    }
}

为什么这样有问题?

因为增加变量的方法实际上并不是增加它的原子操作,所以:

  1. 获取以前的值
  2. 将此值加一
  3. 设定新值

它们不是原子操作的3个操作,您应该给我们一个synchronized块,或者改用AtomicInteger

使用synchronized块,它将类似于:

synchronized (n) {
    temp = n.getN();
    n.setN(temp + 1);
}

使用AtomicInteger您将需要如下重写代码:

class IntCell {
    private final AtomicInteger n = new AtomicInteger();
    public int getN() {return n.get();}
    public void incrementN(int n) {this.n.addAndGet(n);}
}

for (int i = 0; i < 200000; i++) {
    n.incrementN(1);
}

使用AtomicInteger的方法是非阻塞的,因此它将更快

这里的问题是您考虑了比赛条件。 考虑循环内的块:

temp = n.getN();
n.setN(temp + 1);

代码上下文在获取当前N的时间和递增当前N的时间之间切换,从而设置了“旧”值。 解决此问题的一种方法是确保循环的内部部分在同步块中运行:

for (int i = 0; i < 200000; i++) {
    synchronized (n) { / Here!
        temp = n.getN();
        n.setN(temp + 1);
    }
}

当两个线程同时访问一个对象时,它们会相互干扰,结果是不确定的。 例如,假设p读取n的值并得到0,然后q读取相同的值并也得到0,然后p将value设置为1, q也将其设置为1(因为它仍然认为它具有值0)。 现在,即使两个计数器都将其“递增”一次, n的值也将增加1。 您需要使用synchronized块来确保计数器不会相互干扰。 有关更多信息,请参见https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM