我已经读过将参考变量设置为volatile,不会使其内部字段变得不稳定。但是我尝试使用下面的示例,其中看起来volatile性质也适用于类的内部字段。
User.java:- //将字段“flag”设置为true的用户类。
public class User {
private boolean flag=true;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
MyRunnableThread1.java: -
这里我将“user”设为volatile,而不是将其内部字段“flag”设为volatile
子线程在“while(this.user.isFlag())”中连续循环。
public class MyRunnableThread1 implements Runnable {
private String threadName;
private volatile User user;
public MyRunnableThread1(String threadName,User user)
{
this.threadName=threadName;
this.user=user;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public void run() {
System.out.println("child thread:"+threadName+" started");
while(this.user.isFlag()) {
}
System.out.println("child thread:"+threadName+" finished");
}
}
ThreadDemo.java: -
在主线程中,我们将“User”对象的字段“flag”的值设置为false 终止子线程中的循环
public class ThreadDemo {
public static void main(final String[] arguments) throws InterruptedException {
System.out.println("main thread started");
User user=new User();
MyRunnableThread1 myRunnableThread1=new MyRunnableThread1("Thread1",user);
Thread t=new Thread(myRunnableThread1);
t.start();
try {
Thread.sleep(6000);
}
catch(Exception e) {
System.out.println("exception in sleep:"+e);
}
myRunnableThread1.getUser().setFlag(false);
System.out.println("main thread finished");
}
}
O / P: -
主线程开始了 子线程:Thread1启动 主线完了 子线程:Thread1完成
在上面的示例中,我将“user”变量设置为“MyRunnableThread1.java”类中的volatile。 用户对象具有字段“flag”,在实例化时为true。 在启动子线程后,它继续执行循环,主线程将用户对象的字段“flag”的值更改为false。 但是这里字段“flag”不是volatile而不是“user”引用变量是volatile。但是仍然在这里主线程中字段“flag”的值的变化反映在子线程中。它表现得好像字段“flag”也是易挥发。 任何人都可以帮助解决上述问题吗?
答案 0 :(得分:2)
来自JLS:
8.3.1.4。 volatile字段
Java编程语言允许线程访问共享 变量(第17.1节)。通常,确保共享变量 一致且可靠地更新,线程应确保它具有 通过获得锁定来独占使用这些变量, 通常,对这些共享变量实施互斥。
Java编程语言提供了第二种机制,即volatile 字段,这比某些目的的锁定更方便。
字段可以声明为volatile,在这种情况下是Java Memory Model 确保所有线程都看到变量的一致值 (§17.4)。
但是对象不是变量。然后在你的情况下,user
的值是一致的,这意味着所有线程都看到相同的引用,而不是它们的内部内容观察到相同的值。
答案 1 :(得分:0)
您始终通过对实例的引用来访问成员:
this.user.isFlag()
这样,你的循环包含一个易变变量user
的读取,并且循环体没有被优化掉。
尝试在循环之前获取对局部变量的实例引用,并且您将看到差异。
E.g:
User u = this.user;
while(u.isFlag())
{
}
答案 2 :(得分:0)
不要假设声明引用volatile可以保证引用对象成员的安全发布。
当引用对象是可变的并且缺乏线程安全性时,其他线程可能会看到部分构造的对象或处于不一致状态的对象。
当指示物是不可变的时,声明引用volatile
就足以保证指示物成员的安全发布。您不能使用volatile
来保证可变对象的安全发布。使用volatile
只能保证原始字段,对象引用或不可变对象引用字段的安全发布。
只有在一组有限的情况下才能使用volatile变量而不是lock。对于volatile变量,必须满足以下两个条件才能提供所需的线程安全性:
要点:
User
是可变的,Thread
设置flag
值并不能保证新值对另一个Thread
可见。
选项1:使用volatile flag
:
public class User {
private volatile boolean flag = true;
}
选项2:使用AtomicBoolean
:
public class User {
private final AtomicBoolean flag = new AtomicBoolean(true);
public boolean isFlag() {
return flag.get();
}
public void setFlag(boolean flag) {
this.flag.set(flag);
}
}
选项3:使用synchronized
:
public class User {
private boolean flag = true;
public synchronized boolean isFlag() {
return flag;
}
public synchronized void setFlag(boolean flag) {
this.flag = flag;
}
}