java-同步和volatile变量
我从中读到: …同步块还保证在同步块内访问的所有变量都将从主存中读取,并且当线程退出同步块时,所有更新的变量将再次刷新回主存,,而不管该变量是否声明为java-同步和volatile变量,java,concurrency,synchronization,volatile,java-memory-model,Java,Concurrency,Synchronization,Volatile,Java Memory Model,我从中读到: …同步块还保证在同步块内访问的所有变量都将从主存中读取,并且当线程退出同步块时,所有更新的变量将再次刷新回主存,,而不管该变量是否声明为volatile 还有一个例子显示在: 公共类停止线程{ 请求私有静态布尔值; 私有静态同步void requestStop(){ stopRequested=true; } 私有静态同步布尔stopRequested(){ 要求退货; } 公共静态void main(字符串[]args)引发InterruptedException{ 线程背景线程
volatile
还有一个例子显示在:
公共类停止线程{
请求私有静态布尔值;
私有静态同步void requestStop(){
stopRequested=true;
}
私有静态同步布尔stopRequested(){
要求退货;
}
公共静态void main(字符串[]args)引发InterruptedException{
线程背景线程=新线程(()->{
int i=0;
而(!stopRequested())i++
});
backgroundThread.start();
时间单位。秒。睡眠(1);
请求停止();
}
}
变量stopRequested
未声明为volatile
,作者声明“……换句话说,这些方法上的同步仅用于其通信效果,而非互斥……”。但我想知道,在访问数组的元素或访问同步的方法/块中对象的字段时,我们能否始终保证内存可见性,,而不必手动强制对数组元素进行易失性访问(例如使用Unsafe#getObjectVolatile
或在对象字段上声明volatile
修饰符?谢谢
//flags是自定义类的实例。flags.stopRequested是否应声明为volatile?
公共类停止线程{
私有静态标志Flags=新标志();
私有静态同步void requestStop(){
flags.stopRequested=true;
}
私有静态同步布尔stopRequested(){
返回标志。停止请求;
}
}
//标志是一个数组。我们应该使用getObjectVolatile/putObjectVolatile来访问元素吗?
公共类停止线程{
私有静态布尔[]标志=新布尔[n];
私有静态同步void requestStop(){
标志[m]=真;
}
私有静态同步布尔stopRequested(){
返回标志[m];
}
}
在第一个示例中,使用静态
初始化器初始化标志。Java内存模型保证任何后续读取都会看到引用的更新值,并且标志
的正确初始状态(基本上,标志
将正确发布)
但是,由于标志
是可变的,并且可能在稍后的时间点由多个线程进行变异,因此需要使用适当的同步来确保其状态的内存可见性。因此,其字段需要使用易失性
(或适当的同步)
在第二个示例中,简单地将标志
声明为volatile
不会确保写入数组的内存可见性。它只会确保a发生在关系b/w写入数组引用和后续读取之前。要确保a发生在关系b/w写入数组元素和后续读取数组元素之前,您需要使用锁定,您已经在这样做了
为什么会这样?JMM保证在关系b/w释放监视器及其重新获取之前发生。当一个线程释放一个锁,而该锁后来被另一个线程获取时,一种总排序(由before控制)确保前一个线程中发生的任何写入以及重新获取锁的线程的任何后续读取
请记住,将引用声明为volatile并不能确保它所引用的对象的可变状态的正确可见性。我们仍然需要适当的同步机制来确保内存可见性。非常感谢您的回答!因此在我的理解中,如果两个示例中的标志
只能是acces通过两种synchronized
方法(requestStop
和stopRequested
),将不需要声明标志。stopRequested
或布尔[]将
标记为volatile
,也不使用getObjectVolatile
或putObjectVolatile
访问数组,因为synchronized
关键字已经保证了线程之间的“发生在之前”关系。对吗?确切地说,不需要使用volatile。volatile对prope来说是一个较弱的原语r同步,并且您已经使用了正确的同步。Re,“同步块…保证所有变量…将从主存中读取,并且…所有更新的变量将刷新回主存”这当然是一种可能的实现,但实际的保证比这更一般。你可以这样总结:Java语言规范保证一个线程在离开一个同步块之前对共享变量所做的任何事情都会对随后进入一个同步块的任何其他线程可见同一个锁对象。@SolomonSlow谢谢你的提示