While循环和检查静态变量

3

我有两个线程,在一个线程中我设置了静态变量,在另一个线程中,我通过函数检查静态变量,如下所示:

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");

临时类

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);
}

你在哪里调用了 setVarSet() 函数? - Braj
正常工作。它没有挂起程序。 - Braj
@Braj 我已经编辑了代码。 - user2022068
@P82 - 你确定 run() 方法被调用了吗?你是否在那里设置了断点并检查了它在两种情况下是否到达了? - Avi
可能是重复的问题:循环没有打印语句就看不到变化的值 - Boann
显示剩余2条评论
4个回答

5
你没有公布 TempisVarSet 中发生了什么,但很可能你改变了一个变量。这个变量必须被标记为volatile
如果你的类看起来像这样:
public class Temp {
   private static boolean someFlag;

   public static boolean isVarSet() {
      return someFlag;
   }
}

如果您的循环与示例相同,编译器会认为无需重复读取标志,因为循环内部未更改标志,从而优化以避免重复读取标志。

someFlag标记为volatile

private static volatile boolean someFlag;

将强制运行时在每次迭代中检查标志,而不仅仅是假设该值未更改。在这种情况下,它将起作用。
来自Oracle关于原子访问的文档

原子操作不能交错,因此可以在不担心线程干扰的情况下使用。但是,这并不能消除所有同步原子操作的需要,因为仍然可能存在内存一致性错误。使用volatile变量可以降低内存一致性错误的风险,因为对volatile变量的任何写入都会与对该变量的后续读取建立happens-before关系。这意味着对volatile变量的更改始终对其他线程可见。更重要的是,它还意味着当线程读取volatile变量时,它不仅看到volatile的最新更改,还看到导致更改的代码的副作用。


@Avi 如果原帖使用synchronized同步读写操作,那么volatile就不会有任何影响。 - John Vint
@JohnVint - OP在没有分享Temp的代码的情况下发布了问题。(他/她)只是在我的回答之后才添加了它。 - Avi
@JohnVint - 谢谢 ;) - Avi

2
  1. 即使将变量设置为volatile,仍然需要添加SOP在while循环中才能工作。
  2. 这两个用例让我产生了另一种想法。试试看。

由于您的读写方法是同步的,在while循环中:

while(!Temp.isVarSet()){
}

调用该方法除了方法本身以外不会有其他操作,可能会导致同步方法在Temp对象上持有锁,从而不允许其他线程通过同步setMethod修改其值。

如果在while循环中添加SOP,则它会在IO上进行一些工作,从而允许一些时间片段给另一个线程获取Temp的锁并修改同样的值。

请尝试从read方法中删除同步操作,仅供测试目的,并发布您的结果。

public class Temp {

    private volatile static boolean varSet=false;

    public synchronized static void setVarSet() {
        Temp.varSet=true;
    }

    public  static boolean isVarSet() {
        return Temp.varSet;
    }
}

没有,去掉同步并没有起作用。唯一有效的方法是 Thread.sleep(N)。 - user2022068

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;
        }
    }

}

请您发布一个完整的示例?

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();
        }
    });
}

感谢您的时间。我已经编辑了 - 请查看编辑部分。 - user2022068

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接