jvm 为什么线会留下线?

blmhpbnm  于 2022-11-07  发布在  其他
关注(0)|答案(1)|浏览(130)

我是编程新手,研究线程已经有一段时间了。
因此,下列程式码应该会给予的输出为:

one 98098
two 98099

有时候确实如此。
当我试着运行它几次时,它给出了不同的输出。我可以理解JVM控制线程,我不能直接影响它,但是一些输出小于98,000,即使for循环98次增加1000。这是怎么发生的?线程会留下行吗?或者我做错了什么(注意:预期输出有时会显示在屏幕上,但并不总是如此)

public class TestThreads {
    public static void main(String [] args) {
        ThreadOne t1 = new ThreadOne();
        Thread one = new Thread(t1);
        ThreadTwo t2 = new ThreadTwo();
        Thread two = new Thread(t2);
        one.start();    
        two.start();
    }
}
class Accum {
    private int counter = 0;
    private static Accum a = new Accum();
    private Accum() {
    }
    public static Accum getAccum() {
        return a;
    }
    public int getCount() {
        return counter;
    }
    public void updateCounter(int add) {
        counter += add;
    }
}

class ThreadOne implements Runnable {
    Accum a = Accum.getAccum();
    public void run() {
        for(int x=0; x < 98; x++) {
            a.updateCounter(1000);
            try {
                Thread.sleep(50);
            } catch(InterruptedException ex) { }
        }
        System.out.println("one "+a.getCount());
    }
}

class ThreadTwo implements Runnable {
    Accum a = Accum.getAccum();
    public void run() {
        for(int x=0; x < 99; x++) {
            a.updateCounter(1);
            try {
                Thread.sleep(50);
            } catch(InterruptedException ex) { }
        }
        System.out.println("two "+a.getCount());
    }
}
ggazkfy8

ggazkfy81#

基本上,你的updateCounter方法不是线程安全的,如果同时从两个线程调用它,你可能会丢失信息。
让我们重写一下,让它更清楚地说明为什么会出现这种情况:

public void updateCounter(int add) {
    // Fetch
    int originalValue = counter;
    // Compute
    int newValue = originalValue + add;
    // Store
    counter = newValue;
}

想象一下,如果两个线程同时进入该方法,会发生什么情况。我们将假设发生的事情存在某种“总排序”--实际情况比这更复杂,但即使是简化形式也会显示问题。假设counter的值开始为5,在线程x上我们调用updateCounter(3),在线程y上我们调用updateCounter(4)。我们可以想象这一系列事件:
1.线程x执行“fetch”操作:originalValue = 5(局部变量,不受线程y影响)
1.线程y执行“fetch”操作:originalValue = 5
1.线程x执行“计算”操作:newValue = 8
1.线程y执行“计算”操作:newValue = 9
1.线程x执行“存储”操作:counter = 8(请注意,线程x中的newValue与线程y中的newValue是分开的)
1.线程y执行“存储”操作:counter = 9
因此,我们最终得到counter的值为9......就好像updateCounter(3)调用从未发生过一样。如果最后两个操作以相反的顺序发生,那么counter将改为8。
解决此问题的方法是使用AtomicInteger类,该类专门设计用于执行类似于 atomic 的操作:

class Accum {
    private final AtomicInteger counter = new AtomicInteger(0);
    private static Accum a = new Accum();

    private Accum() {
    }

    public static Accum getAccum() {
        return a;
    }

    public int getCount() {
        return counter.get();
    }

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

相关问题