我想编写两个递增数字并减少数字的线程,以及一个确定两个数字何时相等的主线程。例如,一个数字从0开始,另一个数字从10开始...当它们都是5时,主线程应该识别它们是相等的并打印"它们相遇!"。
在此代码中,主线程无法成功比较numup
和numdown
:
public class Number implements Runnable {
public static int numup = 0;
public static int numdown = 10;
public Number() {
}
public static void main(String args[]) {
Number number = new Number();
Thread T1 = new Thread(number, "up");
Thread T2 = new Thread(number, "down");
T1.start();
T2.start();
while (true) {
if (numup == 5 && numdown == 5) {
System.out.println("Meet!");
System.exit(0);
}
}
}
public void run() {
while (true) {
if (Thread.currentThread().getName().equals("up")) {
numup++;
System.out.println(numup);
} else if (Thread.currentThread().getName().equals("down")) {
numdown--;
System.out.println(numdown);
}
try {
Thread.sleep(1000);
} catch (Exception e) {
System.out.println("wake!");
}
}
}
}
失败的结果:
1
9
8
2
7
3
6
4
五
五
6
4
7
3
8
2
1
9
但是,当我让主线程休眠几毫秒时,它可以工作:
public class Number implements Runnable {
public static int numup = 0;
public static int numdown = 10;
public Number() {
}
public static void main(String args[]) {
Number number = new Number();
Thread T1 = new Thread(number, "up");
Thread T2 = new Thread(number, "down");
T1.start();
T2.start();
while (true) {
try {
Thread.sleep(10);
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + "was waked!");
}
if (numup == 5 && numdown == 5) {
System.out.println("They Meet!");
System.exit(0);
}
}
}
public void run() {
while (true) {
if (Thread.currentThread().getName().equals("up")) {
numup++;
System.out.println(numup);
} else if (Thread.currentThread().getName().equals("down")) {
numdown--;
System.out.println(numdown);
}
try {
Thread.sleep(1000);
} catch (Exception e) {
System.out.println("wake!");
}
}
}
}
成功的结果:
1
9
2
8
3
7
4
6
五
五
他们见面了!
为什么增加的延迟使其有效?
答案 0 :(得分:2)
这可能是因为CPU缓存。当数字线程更新变量的值(这从其CPU缓存到主存储器)时,相应主线程的CPU缓存可能没有更新。
因此,当主线程检查变量的值时,它仍然是旧值。
您可以参考此link。
在线程操作非易失性变量的多线程应用程序中,出于性能原因,每个线程可以在处理它们时将变量从主存储器复制到CPU缓存中。如果您的计算机包含多个CPU,则每个线程可以在不同的CPU上运行。这意味着,每个线程可以将变量复制到不同CPU的CPU缓存中。
对于非易失性变量,无法保证Java虚拟机(JVM)何时将数据从主内存读取到CPU缓存中,或将数据从CPU缓存写入主内存。
<强>易失性:强>
public static volatile int numup = 0;
public static volatile int numdown = 10;
原子整数:
import java.util.concurrent.atomic.AtomicInteger;
public class Number implements Runnable {
public static AtomicInteger numup = new AtomicInteger(0);
public static AtomicInteger numdown = new AtomicInteger(10);
public Number() {
}
public static void main(String args[]) {
Number number = new Number();
Thread T1 = new Thread(number, "up");
Thread T2 = new Thread(number, "down");
T1.start();
T2.start();
while (true) {
if (numup.get() == 5 && numdown.get() == 5) {
System.out.println("Meet!");
System.exit(0);
}
}
}
public void run() {
while (true) {
if (Thread.currentThread().getName().equals("up")) {
numup.incrementAndGet();
System.out.println(numup);
} else if (Thread.currentThread().getName().equals("down")) {
numdown.decrementAndGet();
System.out.println(numdown);
}
try {
Thread.sleep(1000);
} catch (Exception e) {
System.out.println("wake!");
}
}
}
}
答案 1 :(得分:1)
快速回答 - 将volatile修饰符添加到numdown和numup。
答案很长: 你的问题是,由于以下原因,其他线程无法看到numdown和numup已经改变:
因此,当你引入一个volatile变量时,java保证从一个线程写入将发生发生 - 之前与读取的关系形成另一个线程,从而使更改对另一个线程可见。在更低级别,它可能会引入内存屏障
无论如何,它不适合SO答案来正确解释它是如何工作的,但如果你有兴趣深入研究这个话题,你可以阅读/观看一些优秀的资源。
干杯!
答案 2 :(得分:0)
有趣的人和叶戈尔给出的好答案。只是添加我的观察结果,即使您在if (numup == 5 && numdown == 5)
方法的while loop
内编写run()
支票,程序也会暂停。
如果您想试用volatile关键字
public static volatile int numup = 0;
public static volatile int numdown = 10;
volatile关键字将确保您的线程不会缓存变量的值,并始终从主内存中检索它。