我有两个线程,在一个线程中我设置静态变量,在另一个线程中我通过这样的函数检查静态变量
Test test= new Test();
while(!Temp.isVarSet()){
}
System.out.println("Variable set");
但这个代码挂了 - 没有去println语句。但是以下代码可以正常工作
Test test= new Test();
while(!Temp.isVarSet()){
System.out.println("I am still here");
}
System.out.println("Variable set");
Temp类
public class Temp {
private volatile static boolean varSet=false;
public synchronized static void setVarSet() {
Temp.varSet=true;
}
public synchronized static boolean isVarSet() {
return Temp.varSet;
}
}
测试课
public class Test{
public Test() {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
Model model= new Model();
View view = new View();
Controller controller=new Controller(model, view);
Temp.setVarSet();
...
}
});
}
}
可能是什么原因?我设置方法isVarSet()同步,但它没有帮助 的修改 这段代码也有效。
Test test = Test()
while(!Temp.isVarSet()){
Thread.sleep(100);
}
答案 0 :(得分:4)
您没有发布Temp
和isVarSet
中发生的情况,但很可能是您更改了变量。此变量必须标记为volatile
。
如果您的课程如下:
public class Temp {
private static boolean someFlag;
public static boolean isVarSet() {
return someFlag;
}
}
你的循环与示例相同,编译器认为不需要反复读取标志,因为循环内的标志没有改变,并且它优化为不反复读取标志。 / p>
将someFlag
标记为volatile
:
private static volatile boolean someFlag;
将强制运行时检查每次迭代时的标志,而不是仅仅假设该值未更改。在这种情况下,它会起作用。
来自Oracle docs about atomic access:
原子动作不能交错,因此可以毫无顾虑地使用它们 线程干扰。但是,这并不能消除所有需要 同步原子操作,因为内存一致性错误 还有可能。使用volatile变量可降低内存风险 一致性错误,因为任何写入volatile变量 建立与之后发生的关系 同一个变量。这意味着对volatile变量的更改是 其他线程始终可见。更重要的是,它也意味着什么时候 一个线程读取一个volatile变量,它不仅看到最新的变化 对于挥发性,还有导致代码的副作用 变化
答案 1 :(得分:2)
这两个用例给了我另一个想法。试试吧。
由于您的读写方法是同步的,因此在您的while循环中
while(!Temp.isVarSet()){
}
除了调用方法之外没什么可做的,这个同步方法可能会对Temp对象保持锁定,这不允许其他线程修改值(虽然同步setMethod)。
虽然在内部添加SOP,但它正在对IO做一些工作,因此它允许一些时间片到其他线程获取Temp的锁并修改它。
您可以尝试从读取方法中删除同步,仅用于测试目的并发布结果。
public class Temp {
private volatile static boolean varSet=false;
public synchronized static void setVarSet() {
Temp.varSet=true;
}
public static boolean isVarSet() {
return Temp.varSet;
}
}
答案 2 :(得分:1)
这对我来说非常适合:
public class ThreadTest {
public static void main(String[] args) throws Exception {
Thread t1 = new TheThread();
t1.start();
// wait
Thread.sleep(500);
System.out.println(Thread.currentThread().getId() + " will now setVarSet()");
Temp.setVarSet();
System.out.println(Thread.currentThread().getId() + " setVarSet() setted");
t1.join();
System.out.println(Thread.currentThread().getId() + " end programm");
}
private static class TheThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + " enter run");
while (!Temp.isVarSet()) {
System.out.println(Thread.currentThread().getId() + " running");
try {
Thread.sleep((int) (Math.random() * 100));
} catch (InterruptedException e) {
// ignore
}
}
System.out.println(Thread.currentThread().getId() + " exit run");
}
}
private static class Temp {
private volatile static boolean varSet = false;
public static void setVarSet() {
Temp.varSet = true;
}
public static boolean isVarSet() {
return Temp.varSet;
}
}
}
你能发一个完整的例子吗?
答案 3 :(得分:1)
它按预期工作而不会挂起程序。
private volatile static boolean varSet = false;
public synchronized static void setVarSet() {
varSet = true;
}
public synchronized static boolean isVarSet() {
return varSet;
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while (!TestDemo.isVarSet()) {
// System.out.println("I am still here");
}
System.out.println("Variable set");
}
});
t1.start();
Thread.sleep(1000); // put delay to give the chance to execute above thread
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
// Model model= new Model();
// View view = new View();
// Controller controller=new Controller(model, view);
setVarSet();
}
});
}