Java中的易变关键字 – 澄清
我真的很困惑我读了关于在Java中的volatile关键字的应用程序。
-
以下说法正确吗? “写入到一个易失性字段发生在每个后续读取相同的字段”
-
理想情况下,何时应该使用volatile关键字
-
有什么区别:
class TestClass { private int x; synchronized int get(){return x;} synchronized void set(int x){this.x = x;} }
和
class TestClass { private volatile int x; int get(){return x;} void set(int x){this.x = x;} }
volatile是一个字段修饰符 , 同步修改代码块和方法 。 所以我们可以使用这两个关键字指定一个简单访问器的三个变体:
int i1; int geti1() {return i1;} volatile int i2; int geti2() {return i2;} int i3; synchronized int geti3() {return i3;}
geti1()
访问当前线程中当前存储在i1
中的值。 线程可以有variables的本地副本,数据不必与其他线程中保存的数据相同。特别是,另一个线程可能在其线程中更新了i1
,但是当前线程中的值可能不同于那更新的价值。 事实上,Java有一个“主”内存的概念,这是为variables保存当前“正确”值的内存。 线程可以有自己的variables副本,线程副本可以不同于“主”内存。 所以事实上,对于i1
,“主”存储器的值可能是1,对于i1
,如果thread1和thread2都更新了,则thread1对于i1
具有值2,对于i1
具有值3 i1但这些更新的值尚未传播到“主”内存或其他线程。
另一方面, geti2()
有效地从“main”内存访问i2
的值。 一个volatilevariables不允许有一个variables的本地副本,这个variables与当前保存在“main”内存中的值不同。 实际上,一个声明为volatile的variables必须使数据在所有线程中保持同步,以便每当访问或更新任何线程中的variables时,所有其他线程立即看到相同的值。 一般来说,volatilevariables比“plain”variables具有更高的访问和更新开销。 一般来说,线程可以拥有自己的数据副本,以提高效率。
volitile和synchronized有两个区别。
首先同步获取并释放监视器上的锁,一次只能强制一个线程执行代码块。 这是众所周知的同步方面。 但同步也同步内存。 实际上,同步将整个线程内存与“主”内存同步。 所以执行geti3()
会执行以下操作:
- 线程获取监视器上对象的锁。
- 线程内存刷新其所有variables,即它的所有variables都能从“主”内存中有效地读取。
- 代码块被执行(在这种情况下,将返回值设置为i3的当前值,可能刚从“主”存储器复位)。
- (对variables的任何更改现在通常会写入“主”内存,但对于geti3(),我们没有任何更改。)
- 线程释放监视器上的锁对象this。
因此,volatile只在同步线程内存和“main”内存之间的一个variables的值时,同步所有variables在线程内存和“main”内存之间的同步,并locking和释放一个监视器来引导。 明显的同步可能会有更多的开销比易变。
http://javaexp.blogspot.com/2007/12/difference-between-volatile-and.html
volatile
保证从variables读取总是反映最新的更新值。 运行时可以通过各种方式实现,包括在值更改时不caching或刷新caching。
bwawok躲过去了,但是volatile关键字只是为了内存的可见性。 在Java 1.5发布之前,volatile关键字声明了这个字段会每次点击主内存来获取对象的最新值,以便读取和刷新写入。
今天的易变关键字是两个非常重要的事情:
- 不要担心如何,但知道,当阅读一个易变的领域,你将永远拥有最新的价值。
- 编译器不能重新命令易失性读/写来保持程序的顺序。
从客户端的angular度来看,私有易失性字段对公共接口是隐藏的,而同步方法则更加可见。
回答你的问题的第三部分,部分是第二部分。
synchronized
和volatile
样本之间没有function差异。
然而,每个人在性能方面都有自己的缺点。 在某些情况下, volatile
性能可能比使用java.util.concurrent
synchronized
或其他基元更糟糕。 对于这个讨论请参阅 – > 为什么Java中的variables默认是volatile? 。
通过回答KeremBaydoğan是完全正确的。 我只想举一个关于volatile
关键字提供给我们的例子。
首先,我们有一个柜台,像这样
public class Counter { private int x; public int getX() { return x; } public void increment() { x++; } }
还有一些增加x值的Runnable任务
@Override public void run() { for (N) { int oldValue = counter.getX(); counter.increment(); int new value = counter.getX(); } } }
没有同步会有线程之间的干扰 ,只是不会工作
最简单的方法来解决这个问题:
public class Counter { private int x; public synchronized int getX() { return x; } public synchronized void increment() { x++; } }
其实为了迫使系统破解,我在读写x
之前做了一个Thread.sleep
,试想一下是BD还是一个巨大的任务来处理。
现在什么是volatile
有用的? 那里有很多好的文章: 易变的文章或这个问题
同步对公共资源的访问不是答案,但是保持标志停止线程是一个不错的select
我是我们的先行者 例如,假设我们想将variables增加到100,一个简单的方法可能是一个volatile boolean
标志。 例:
private volatile boolean stop; @Override public void run() { while(!stop) { int oldValue = counter.getX(); if (oldValue == 100) { stop = true; } else { counter.increment(); int new value = counter.getX(); } } }
这工作正常,但是,如果您从标志中删除volatile
关键字,可能会遇到和无限循环。