对volatile,看到了个很清楚的解释,摘录下来如下:
volatile关键字有什么用?
恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。volatile是变量修饰符,而synchronized则作用于一段代码或方法;看如下三句get代码:
- inti1; intgeti1(){returni1;}
- volatile inti2; intgeti2(){returni2;}
- int i3; synchronizedintgeti3(){returni3;}
geti1()得到存储在当前线程中i1的数值。多个线程有多个i1变量拷贝,而且这些i1之间可以互不相同。换句话说,另一个线程可能已经改
变了它线程内的i1值,而这个值可以和当前线程中的i1值不相同。事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程
可以有它自己的变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的i1值是1,线程1里的i1值
是2,线程2里的i1值是3——这在线程1和线程2都改变了它们各自的i1值,而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。
而geti2()得到的是“主”内存区域的i2数值。用volatile修饰后的变量不允许有不同于“主”内存区域的变量拷贝。换句话说,一个变量经
volatile修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。理所当然的,volatile修饰的变量
存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。
既然volatile关键字已经实现了线程间数据同步,又要synchronized干什么呢?呵呵,它们之间有两点不同。首
先,synchronized获得并释放监视器——如果两个线程使用了同一个对象锁,监视器能强制保证代码块同时只被一个线程所执行——这是众所周知的事
实。但是,synchronized也同步内存:事实上,synchronized在“主”内存区域同步整个线程的内存。因此,执行geti3()方法做
了如下几步:
1. 线程请求获得监视this对象的对象锁(假设未被锁,否则线程等待直到锁释放)
2. 线程内存的数据被消除,从“主”内存区域中读入(Java虚拟机能优化此步。。。[后面的不知道怎么表达,汗])
3. 代码块被执行
4. 对于变量的任何改变现在可以安全地写到“主”内存区域中(不过geti3()方法不会改变变量值)
5. 线程释放监视this对象的对象锁
因此volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。显然synchronized要比volatile消耗更多资源。
附英文原文:
What does volatile do?
This is probably best explained by comparing the effects that
volatile and synchronized have on a method. volatile is a field
modifier, while synchronized modifies code blocks and methods. So we
can specify three variations of a simple accessor using those two
keywords:
- inti1; intgeti1(){returni1;}
- volatile inti2; intgeti2(){returni2;}
- int i3; synchronizedintgeti3(){returni3;}
geti1() accesses the value currently stored in i1 in the current
thread. Threads can have local copies of variables, and the data does
not have to be the same as the data held in other threads. In
particular, another thread may have updated i1 in it’s thread, but the
value in the current thread could be different from that updated value.
In fact Java has the idea of a “main” memory, and this is the memory
that holds the current “correct” value for variables. Threads can have
their own copy of data for variables, and the thread copy can be
different from the “main” memory. So in fact, it is possible for the
“main” memory to have a value of 1 for i1, for thread1 to have a value
of 2 for i1 and for thread2 to have a value of 3 for i1 if thread1 and
thread2 have both updated i1 but those updated value has not yet been
propagated to “main” memory or other threads.
On the other hand, geti2() effectively accesses the value of i2 from
“main” memory. A volatile variable is not allowed to have a local copy
of a variable that is different from the value currently held in “main”
memory. Effectively, a variable declared volatile must have it’s data
synchronized across all threads, so that whenever you access or update
the variable in any thread, all other threads immediately see the same
value. Of course, it is likely that volatile variables have a higher
access and update overhead than “plain” variables, since the reason
threads can have their own copy of data is for better efficiency.
Well if volatile already synchronizes data across threads, what is
synchronized for? Well there are two differences. Firstly synchronized
obtains and releases locks on monitors which can force only one thread
at a time to execute a code block, if both threads use the same monitor
(effectively the same object lock). That’s the fairly well known aspect
to synchronized. But synchronized also synchronizes memory. In fact
synchronized synchronizes the whole of thread memory with “main”
memory. So executing geti3() does the following:
1. The thread acquires the lock on the monitor for object this
(assuming the monitor is unlocked, otherwise the thread waits until the
monitor is unlocked).
2. The thread memory flushes all its variables, i.e. it has all of its
variables effectively read from “main” memory (JVMs can use dirty sets
to optimize this so that only “dirty” variables are flushed, but
conceptually this is the same. See section 17.9 of the Java language
specification).
3. The code block is executed (in this case setting the return value to
the current value of i3, which may have just been reset from “main”
memory).
4. (Any changes to variables would normally now be written out to “main” memory, but for geti3() we have no changes.)
5. The thread releases the lock on the monitor for object this.
So where volatile only synchronizes the value of one variable
between thread memory and “main” memory, synchronized synchronizes the
value of all variables between thread memory and “main” memory, and
locks and releases a monitor to boot. Clearly synchronized is likely to
have more overhead than volatile.
摘自:http://bianbian.org/technology/java/88.html