两个线程引用一个变量

时间:2017-02-28 14:33:49

标签: java multithreading java-threads

我正在运行以下课程。

public class RunThreads implements Runnable {
    static int  i;
    public static void main(String[] args) {
        RunThreads job = new RunThreads();
        Thread alpha = new Thread(job);
        Thread beta = new Thread(job);
        alpha.setName("Alpha");
        beta.setName("beta");
        alpha.start();
        beta.start();
    }
    public void run(){
        for(;i<10;i++){
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}

我的输出是:

beta0
beta1
Alpha0
beta2
beta4
beta5
beta6
Alpha3
Alpha8
beta7
Alpha9

我知道每次执行它都会得到不同的输出。我的问题是,对于i0线程,alpha和{{1},为什么输出的值betaAlpha0两倍? }}。

beta0线程将i的值增加到1。那么,beta线程如何打印alpha

我可能会遗漏一些非常明显的东西。谢谢!

4 个答案:

答案 0 :(得分:3)

当您在没有同步的情况下访问共享数据时,事情是可怕的:

  • 无法保证Alpha线程阅读i会看到&#34;最新的&#34;从beta线程更新,反之亦然
  • 两个主题可以在大致相同的时间启动i++ ......而i++基本上是:

    int tmp = i;
    tmp++;
    i = tmp;
    

    你很容易失去&#34;增量

如果您想使此线程安全,则应使用AtomicInteger代替:

import java.util.concurrent.atomic.AtomicInteger;

public class RunThreads implements Runnable {
    static AtomicInteger counter = new AtomicInteger();
    public static void main(String[] args) {
        RunThreads job = new RunThreads();
        Thread alpha = new Thread(job);
        Thread beta = new Thread(job);
        alpha.setName("Alpha");
        beta.setName("beta");
        alpha.start();
        beta.start();
    }
    public void run(){
        int local;
        while ((local = counter.getAndIncrement()) < 10) {
            System.out.println(Thread.currentThread().getName() + local);
        }
    }
}

您的输出可能仍然显示错误的顺序(因为Alpha线程可能&#34;开始&#34;写入&#34; Alpha0&#34;而beta线程&# 34;开始&#34;写&#34; beta1&#34;但是beta线程首先锁定控制台输出,但是你只能看到每个计数一次。请注意,您必须使用getAndIncrement()的结果进行检查和打印 - 如果您在循环体中调用了counter.get(),则由于操作的交错,您仍然可以看到重复项。 / p>

答案 1 :(得分:1)

这里的简单答案是,无论变量是易失性还是​​原子或其他,当变量值为0时,两个线程都会启动,而打印后,只会更改 <。 / p>

这意味着两个线程都可以到达&#34; print&#34;在其中任何一个到达i++之前的行。

这意味着两者都很可能打印0,除非其中一个被延迟足够长时间以便另一个更新变量(此时出现内存模型和数据可见性的问题)。

答案 2 :(得分:0)

“当”线程A正在进行更新时,线程B正在读取该值。

喜欢在:

A: prints i (current value 0)
B: prints i (current value 0)
A: stores 1 to i

您不能假设那些两个操作:

for(;i<10;i++){ // increasing the loop counter

System.out.println(Thread.currentThread().getName() + i);

打印循环计数器发生在“一次性”中。与此相反的。

两个线程并行执行相应的指令;并且绝对无法保证结果。

答案 3 :(得分:0)

线程可能在多核架构中的不同内核上运行,并且每个内核都有自己的注册表集或本地缓存,只要您不使缓存无效,正在运行的内核可能无法访问RAM内存以获得最新版本日期值,将使用缓存的。为了使它按预期工作,变量必须标记为volatile,这会在每次写入此内存位置时使缓存无效,并直接从RAM中获取值。

更新: 正如在注释中指出的那样 - volatile不足以使值保持最新,i ++上有读写操作。为了使它工作AtomicInteger就足够了,或者更加昂贵地锁定i ++。