Java中volatile关键字的最简单易懂示例
我正在阅读Java中的volatile关键字,完全理解其中的理论部分 但是,我要搜索的是一个很好的例子,它显示了若变量不是易变的,若它是易变的,会发生什么 以下代码段未按预期工作(取自): 理想情况下,如果Java中volatile关键字的最简单易懂示例,java,multithreading,concurrency,volatile,Java,Multithreading,Concurrency,Volatile,我正在阅读Java中的volatile关键字,完全理解其中的理论部分 但是,我要搜索的是一个很好的例子,它显示了若变量不是易变的,若它是易变的,会发生什么 以下代码段未按预期工作(取自): 理想情况下,如果keepRunning不是易失性的,线程应该无限期地继续运行。但是,几秒钟后它确实停止了 我有两个基本问题: 有人能举例说明吗?不符合JLS的理论 volatile是同步的替代品吗?它实现原子性了吗 理想情况下,若keepRunning不是易变的,那个么线程应该无限期地继续运行。但是,几秒
keepRunning
不是易失性的,线程应该无限期地继续运行。但是,几秒钟后它确实停止了
我有两个基本问题:
- 有人能举例说明吗?不符合JLS的理论
- volatile是同步的替代品吗?它实现原子性了吗
volatile
并不意味着内存不会被共享,但是JVM正试图不同步内存(如果可能的话),因为性能原因,所以内存可能不会被更新
另一件需要注意的事情是,System.out.println(…)
是同步的,因为底层的PrintStream
执行同步以停止重叠输出。因此,您可以在主线程中“免费”获得内存同步。然而,这仍然不能解释为什么阅读循环会看到更新
无论println(…)
行是输入还是输出,您的程序都会在带有Intel i7的MacBook Pro上的Java6下为我旋转
有人能举例说明吗?不符合JLS的理论
我认为你的例子很好。不确定为什么它不能与所有已删除的System.out.println(…)
语句一起工作。它对我有用
volatile是同步的替代品吗?它实现原子性了吗
就内存同步而言,volatile
抛出的内存屏障与synchronized
块相同,只是volatile
屏障是单向的,而不是双向的<代码>易失性读取会抛出负载屏障,而写入会抛出存储屏障。synchronized
块是一个双向屏障,添加了互斥锁
然而,就原子性而言,答案是“视情况而定”。如果正在从字段读取或写入值,则
volatile
提供了适当的原子性。但是,递增volatile
字段会受到限制,即++
实际上是3个操作:读取、递增、写入。在这种情况下或更复杂的互斥情况下,可能需要一个完整的synchronized
块AtomicInteger
通过复杂的测试和设置自旋循环解决了++
问题。volatile
不一定会产生巨大的变化,这取决于JVM和编译器。然而,对于许多(边缘)情况,可能是优化导致变量的更改未被注意到与正确写入变量之间的差异
基本上,优化器可以选择将非易失性变量放在寄存器或堆栈上。如果另一个线程在堆或类的原语中更改它们,那么另一个线程将继续在堆栈中查找它们,并且它将过时
volatile
确保不会发生此类优化,所有读写操作都直接到堆或其他所有线程都可以看到它的地方。当变量是volatile
时,它保证不会缓存它,并且不同的线程将看到更新的值。但是,不将其标记为volatile并不保证相反<代码>易失性是JVM中被破坏了很长一段时间的东西之一,但始终没有得到很好的理解。易失性-->保证了可见性而不是原子性
同步(锁定)-->保证可见性和原子性(如果正确完成)
Volatile不能替代同步
仅在更新引用且不对其执行某些其他操作时才使用volatile
例如:
volatile int i = 0;
public void incrementI(){
i++;
}
如果不使用同步或AtomicInteger,将不会是线程安全的,因为递增是一个复合操作
为什么程序不能无限期运行
这取决于各种情况。在大多数情况下,JVM足够智能,可以刷新内容
讨论volatile的各种可能用途。正确使用volatile是很棘手的,我会说“如果有疑问,请忽略它”,使用synchronized block代替
此外:
同步块可以用来代替volatile,但反过来就不正确了 对于您的特定示例:如果未声明为volatile,服务器JVM可以将
keepRunning
变量从循环中提升出来,因为它在循环中未被修改(将其转换为无限循环),但客户端JVM不会。这就是为什么你会看到不同的结果
关于可变变量的一般解释如下:
当一个字段被声明为volatile时,编译器和运行时会注意到该变量是共享的,并且不应使用其他内存操作对其进行重新排序。易失性变量不会缓存在寄存器或缓存中,在这些寄存器或缓存中,易失性变量对其他处理器是隐藏的,因此对易失性变量的读取总是返回任何线程最近的写入
易失性变量的可见性影响超出了易失性变量本身的值。当线程A写入易失性变量,然后线程B读取该变量时,在写入易失性变量之前A可见的所有变量的值都会
volatile int i = 0;
public void incrementI(){
i++;
}
volatile boolean flag;
while (!flag) {
// do something untill flag is true
}
class TestVolatile extends Thread{
//volatile
boolean keepRunning = true;
public void run() {
long count=0;
while (keepRunning) {
count++;
}
System.out.println("Thread terminated." + count);
}
public static void main(String[] args) throws InterruptedException {
TestVolatile t = new TestVolatile();
t.start();
Thread.sleep(1000);
System.out.println("after sleeping in main");
t.keepRunning = false;
t.join();
System.out.println("keepRunning set to " + t.keepRunning);
}
}
public class VolatileDemo {
private static volatile int MY_INT = 0;
public static void main(String[] args) {
ChangeMaker changeMaker = new ChangeMaker();
changeMaker.start();
ChangeListener changeListener = new ChangeListener();
changeListener.start();
}
static class ChangeMaker extends Thread {
@Override
public void run() {
while (MY_INT < 5){
System.out.println("Incrementing MY_INT "+ ++MY_INT);
try{
Thread.sleep(1000);
}catch(InterruptedException exception) {
exception.printStackTrace();
}
}
}
}
static class ChangeListener extends Thread {
int local_value = MY_INT;
@Override
public void run() {
while ( MY_INT < 5){
if( local_value!= MY_INT){
System.out.println("Got Change for MY_INT "+ MY_INT);
local_value = MY_INT;
}
}
}
}
}
class MyThread extends Thread {
private boolean running = true; //non-volatile keyword
public void run() {
while (running) {
System.out.println("hello");
}
}
public void shutdown() {
running = false;
}
}
public class Main {
public static void main(String[] args) {
MyThread obj = new MyThread();
obj.start();
Scanner input = new Scanner(System.in);
input.nextLine();
obj.shutdown();
}
}
private volatile boolean running = true; //volatile keyword
Access_Modifier volatile DataType Variable_Name;
+--------------+--------+-------------------------------------+
| Flag Name | Value | Interpretation |
+--------------+--------+-------------------------------------+
| ACC_VOLATILE | 0x0040 | Declared volatile; cannot be cached.|
+--------------+--------+-------------------------------------+
|ACC_TRANSIENT | 0x0080 | Declared transient; not written or |
| | | read by a persistent object manager.|
+--------------+--------+-------------------------------------+
public class VolatileTest {
private static final Logger LOGGER = MyLoggerFactory.getSimplestLogger();
private static volatile int MY_INT = 0;
public static void main(String[] args) {
new ChangeListener().start();
new ChangeMaker().start();
}
static class ChangeListener extends Thread {
@Override
public void run() {
int local_value = MY_INT;
while ( local_value < 5){
if( local_value!= MY_INT){
LOGGER.log(Level.INFO,"Got Change for MY_INT : {0}", MY_INT);
local_value= MY_INT;
}
}
}
}
static class ChangeMaker extends Thread{
@Override
public void run() {
int local_value = MY_INT;
while (MY_INT <5){
LOGGER.log(Level.INFO, "Incrementing MY_INT to {0}", local_value+1);
MY_INT = ++local_value;
try {
Thread.sleep(500);
} catch (InterruptedException e) { e.printStackTrace(); }
}
}
}
}
public class VolatileDemo {
static class Processor {
//without volatile program keeps running on my platform
private boolean flag = false;
public void setFlag() {
System.out.println("setting flag true");
this.flag = true;
}
public void process() {
while(!flag) {
int x = 5;
// using sleep or sout will end the program without volatile.
// Probably these operations, cause thread to be rescheduled, read from memory. Thus read new flag value and end.
}
System.out.println("Ending");
}
}
public static void main(String[] args) throws InterruptedException {
Processor processor = new Processor();
Thread t1 = new Thread(processor::process);
t1.start();
Thread.sleep(2000);
processor.setFlag();
}
}