我对Java中的线程问题很少。这是代码:
TestingThread类:
public class TestingThread implements Runnable {
Thread t;
volatile boolean pause = true;
String msg;
public TestingThread() {
t = new Thread(this, "Testing thread");
}
public void run() {
while (pause) {
//wait
}
System.out.println(msg);
}
public boolean isPause() {
return pause;
}
public void initMsg() {
msg = "Thread death";
}
public void setPause(boolean pause) {
this.pause = pause;
}
public void start() {
t.start();
}
}
主线程类:
public class Main {
public static void main(String[] args) {
TestingThread testingThread = new TestingThread();
testingThread.start();
testingThread.initMsg();
testingThread.setPause(false);
}
}
问题清单:
t
应volatile
吗? msg
应该volatile
吗?setPause()
应synchronized?
答案 0 :(得分:4)
你的第2号问题非常微妙。
在非常具体的案例中,您:
msg
; pause
; pause
; msg
。因此,您在msg
的写入和读取之间已经过渡性地建立了发生之前的关系。因此msg
本身不一定是易变的。
然而,在现实生活中的代码中,你应该避免依赖于这种微妙的行为:更好的过度volatile
并平静地睡觉。
以下是Java Language Specification的一些相关引用:
如果x和y是同一个线程的动作,并且x在程序顺序中位于y之前,那么hb(x,y)。
如果动作x与后续动作y同步,那么我们也有hb(x,y)。
请注意,在我的操作列表中
至于你的其他问题,
t
不必是volatile,因为它是在创建线程之前编写的,之后从未发生过变化。启动一个线程会导致发生在之前; setPause
不必同步,因为它只是设置了volatile var。答案 1 :(得分:2)
<强>&GT; msg应该是不稳定的吗?
是。它是否必须在这个例子中,不是。但是我还是敦促你使用它,因为代码的正确性变得更加清晰;)请注意,我假设我们正在讨论Java 5或更高版本,之后无论如何都要破坏volatile。
要理解的棘手部分是为什么这个例子可以在没有msg被声明为volatile的情况下逃脱。
考虑main()的这个顺序部分。
testingThread.start(); // starts the other thread
testingThread.initMsg(); // the other thread may or may not be in the
// while loop by now msg may or may not be
// visible to the testingThread yet
// the important thing to note is that either way
// testingThread cannot leave its while loop yet
testingThread.setPause(false); // after this volatile, all data earlier
// will be visible to other threads.
// Thus even though msg was not declared
// volatile it will piggy back the pauses
// use of volatile; as described [here](http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile)
// and now the testingThread can leave
// its while loop
所以如果我们现在考虑testingThread
while (pause) { // pause is volatile, so it will see the change as soon
// as it is made
//wait
}
System.out.println(msg); // this line cannot be reached until the value
// of pause has been set to false by the main
// method. Which under the post Java5
// semantics will guarantee that msg will
// have been updated too.
<强>&GT;应该是不稳定的?
没关系,但我建议将其作为私人决赛。
<强>&GT; setPause()应该同步吗?
在Java 5之前,然后是。在Java 5读取之后,volatile与进入同步块具有相同的内存屏障。写入易失性具有与同步块结束时相同的内存屏障。因此,除非你需要同步块的范围,在这种情况下你不需要,然后你可以使用volatile。
Java 5中对volatile的更改由更改here的作者记录。
答案 2 :(得分:1)
1和2 易失性可以被视为“变量同步”,虽然方式不同,但结果是相似的,以确保它是读取一致的。
3。 我觉得它不需要,因为this.pause = pause应该是一个原子声明。
4。 如果你把Thread.sleep放在里面,这会导致繁忙的等待,这是一个不好的例子(true){什么都不做},这可能会有所帮助。请参阅http://en.wikipedia.org/wiki/Busy_waiting
执行类似“等到被唤醒”之类的更合适的方法之一是使用监视器对象(java中的Object是监视器对象),或者使用条件对象和锁来执行此操作。您可能需要参考http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html
另外,我认为你的自定义Runnable中有一个本地线程文件也不是一个好主意。请参阅Seelenvirtuose的评论。