在Java内存模型中,有main memory(主内存)还每个线程各自的线程内存memory(例如:寄存器)。为了性能一个线程会在自己memory中保持要访问变量的副本。这样就会出现同一个变量在某一个时刻一个线程内存中的值和其他线程内存或者主内存中的值不一致。
一个变量声明为volatile,就意味着这个变量随时会被其他线程修改,因此不能将他cahe在线程memory中,即:不会再memory中保留他的副本,下面看个例子:
public class TestVolatile extends Thread {
private volatile boolean okay;
public void run(){
while(!okay){
System.out.println("current time is okay");
}
}
/**
* 修改通知变量
* @description
* @author Jader
* 2015年5月4日 下午5:18:33
* @version 1.0
*/
public void timeToOkay(){
okay = true;
}
}
此时线程执行run方法的时候不会读取自己的cahe的副本,而是回去主内存中读取,当执行timetoOkay时会通知其他线程,其他线程会去主内存中获取最新的okay。不知道我理解的是否正确
Volatile并不能保证操作的原子性,不像synchronized那样。
最典型的例子是i++,他实际上是由多个原子操作组成,如果多个线程同时执行i++,volatile只能保证他们操作的同一块内存,针对此可以使用java.util.concurrent.atomic.AtomicInteger下的
getAndIncrement()方法实现,具体可以查看API有关concurrent包下的并发类。
volatile一般用来修饰变量,而synchronized是用来修饰代码块或者方法,例如:
int i1;
int geti1() {return i1;}
得到存储在当前线程i1的值,多个线程有多个i1变量的拷贝,而且这些i1的值可以互不相同。上面我们已经提供,多线程环境中分为主内存和线程内存,每个线程都可以有他们自己的变量副本,而这个变量副本可以和主内存中的值不相同,当出现并发的时候线程内存中的i1和线程内存中的i1的值就不相同
volatile int i2;
int geti2()
{return i2;}
此时的i2的值是从主内存中获取,使用volatile修饰后的变量不允许出现主内存和线程内存不一致的情况。任何一个线程改变了变量的值其他线程都会立即获取到相同的值。
int i3;
synchronized int geti3() {return i3;}
synchronized获得并释放监视器,如果两个线程同时使用了同一个对象锁,监听器能强制保证代码块同一时刻只有一个线程能执行
此时geti3其实经过以下步骤:
1、线程请求获得监视this对象的对象锁(如果已经被锁定就需要等待)
2、线程内存数据被清除,从主内存中读入
3、执行代码块geti3()
4、对于变量的任何改动可以安全的写到主内存中
5、线程释放监视this对象的对象锁
通过比较可以看出volatile只是在线程内存和主内存之间同步某个变量的值,而synchronized是通过锁定和解锁某个监视器同步所有的变量,要比volatile消耗更多的资源。
但是volatile关键字修饰的变量不能保证是原子的,诸如i++,i–等
总结:
1、volatile解决了线程间共享变量的可见问题
2、使用volatile会增加性能的开销
3、volatile并不能解决线程同步问题
4、解决i++、i++这种不同步的问题需要使用synchronized、atomicInteger系列的包,也会增加性能开销