我正在运行以下课程。
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
我知道每次执行它都会得到不同的输出。我的问题是,对于i
和0
线程,alpha
和{{1},为什么输出的值beta
为Alpha0
两倍? }}。
beta0
线程将i
的值增加到1
。那么,beta
线程如何打印alpha
我可能会遗漏一些非常明显的东西。谢谢!
答案 0 :(得分:3)
当您在没有同步的情况下访问共享数据时,事情是可怕的:
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;在其中任何一个到达 这意味着两者都很可能打印0,除非其中一个被延迟足够长时间以便另一个更新变量(此时出现内存模型和数据可见性的问题)。i++
之前的行。
答案 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 ++。