本文介绍了Java中的一些基本概念,但非常重要。
volatile是一个字段修饰符,而synchronized修饰了代码块和方法。
因此,我们可以使用这两个关键字指定一个简单访问器的三个变量:
int i1;
int geti1() {return i1;}
volatile int i2;
int geti2() {return i2;}
int i3;
synchronized int geti3() {return i3;}
在上面,我们定义了三个整数变量:i1、i2和i3。我们定义了三个相应的getter方法:geti1()、geti2()和geti3()。
geti1()访问当前线程中存储在i1中的值。
线程可以拥有变量的本地副本,并且数据不必与其他线程中的数据相同。特别是,另一个线程可能在其线程中更新了i1,但是当前线程中的值可能与更新后的值不同。实际上,Java演示了“主”内存的概念,这是存储变量当前“正确”值的内存。线程可以有自己的变量数据副本,线程副本可以不同于“主”内存。
因此,如果thread1和thread2都更新了i1,那么“主”内存的值可能为1,thread1的值为2,thread3的值为3。但是,更新的值还没有传播到“主”内存或其他线程。
另一方面,geti2()有效地从“主”内存访问i2的值。不允许volatile变量具有与当前保存在“主”内存中的值不同的变量的本地副本。实际上,声明为volatile的变量必须在所有线程之间同步它的数据,以便在任何线程中访问或更新变量时,所有其他线程都能立即看到相同的值。通常,volatile变量比普通变量有更高的访问和更新开销。通常,线程被允许拥有自己的数据副本,这是为了提高效率。
volatile和synchronized有两个区别:
首先,synchronized在monitors(监视器)上获得并释放锁,它一次只能强制一个线程执行代码块。这是同步的一个非常著名的方面。但是synchronized也同步了内存。实际上,synchronized使整个线程内存与“主”内存同步。
因此,执行geti3()会执行以下操作:
1、线程为这个对象获取监视器上的锁。
2、线程内存刷新所有的变量,也就是说,它的所有变量都有效地从“主”内存读取。
3、执行代码块。在本例中,这意味着将返回值设置为i3的当前值,i3可能刚刚从“主”内存中重置。
4、对变量的任何更改通常会被写到“主”内存中,但是对于geti3(),我们没有更改。
5、线程释放这个对象在监视器上的锁。
因此,当volatile只在线程内存和“主”内存之间同步一个变量的值时,同步将同步线程内存和“主”内存之间的所有变量的值并锁定,并释放一个监视器来控制多线程之间的所有权。
从这些信息可以得出结论,synchronized比volatile的开销更大。