在我们日常开发中,volatile、synchronized两个关键字我们经常都会遇到。他们两个往往都会和线程相关,所以对于他们的区别自己一定要搞清楚。这里就记录一下它们的区别。
volatile
java中,我们使用volatile关键字修饰一个变量,则不保存拷贝,直接访问主内存。
在java的内存模型中,有主内存(main memory),每一个线程也有自己的memory内存空间(ThreadLocal、寄存器)。为了性能,一个线程会在自己的memory中保存一份要访问的变量的副本。这样就会出现同一个变量在某个瞬时,在一个线程的memory和另一个线程memory中,或者main memory中的值是不一致的情况。为了解决这样的问题,我们就可以为变量加上一个volatile关键字来修饰,这样这个变量就不会被cache在某个线程的memory中,线程对这个变量的访问个修改,都是直接对main memory就行操作。
synchronized
我们使用synchronized来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该代码逻辑。
- 当两个并发线程访问同一个对象Object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
- 当一个线程访问Objec的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该Object中的非synchronized(this)同步代码块。
- 尤其关键的是,当一个线程访问Object中的一个synchronized(this)同步代码块时,其他线程对Object中的所有其他synchronized(this)同步代码块的访问都将被阻塞。
- 当一个线程访问Object的一个synchronized(this)同步代码块时,它就获得了这个Object的对象锁。结果,其他线程对该Object对象所有同步代码部分的访问都被暂时阻塞。
- 以上规则对其他对象锁同样适用。
volatile、synchronized区别
作用域不同
volatile 是一个变量修饰符
synchronized 可以使用在变量、方法、和类级别的性质不同
volatile 只是在线程内存和“主”内存间同步某个变量的值
synchronized 通过锁定和解锁某个监视器同步所有变量的值可见性 原子性
volatile 仅能实现变量的修改可见性,并不能保证原子性
synchronized 则可以保证变量的修改可见性和原子性性能
volatile 不会造成线程的阻塞
synchronized 可能会造成线程的阻塞
synchronized 要比 volatile 消耗更多资源volatile 标记的变量不会被编译器优化,synchronized 标记的变量可以被编译器优化
用法示例
使用volatile关键字的单例模式
private static volatile Singleton instance; // 使用volatile关键字
// 双重锁检验
public static Singleton getInstance() {
if (instance == null) { // 第7行
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // 第10行
}
}
}
return instance;
}