Volatile
只能保证操作的内存可见性和有序性,不能保证对变量的操作是原子性的。
作用:
非常局限的场景下,可以保证原子性
保证变量的内存可见性,被Volatile修饰的变量,每次读,必须从主内存同步;每次写,必须附带写回主内存 (最重要的作用-90%)
非常局限的场景下,可以保护代码重排引起的问题
Synchronized :
可以保证原子性、可见性和有序性。每个 Java 对象都有一个关联的 monitor,使用synchronized 时 JVM 会根据使用环境找到对象的 monitor,根据 monitor 的状态进行加解锁的判断。如果成功加锁就成为该 monitor 的唯一持有者,monitor 在被释放前不能再被其他线程获取。
Java语法:
修饰方法,用在普通方法或者静态方法之前
public synchronized void method() {
}
public static synchronized void staticMethod() {
}
2. 同步代码块
public void someMethod() {
// 同步代码块,用在方法内部
Object o = new Object();
synchronized (o) {
// o 这个引用不能是 null
}
}
Synchronized是如何实现加锁的 - 以同步代码块为例
如果Sync加锁失败,会怎么办?
加锁失败,之后就没有资格继续执行代码了
占据CPU没有意义了
会触发线程调度 ,加锁失败的线程,会被调度器从CPU调度下来
在锁打开之前,再分配CPU给该线程也没有意义
线程状态要变化(不再是Runnable(ready/running))
线程状态变成Blocked(sync加锁失败专用状态)
当准备好了释放锁时,可以找到该线程,把它叫回来
把线程加到这把锁的阻塞队列中(Blocking queue)
Volatile 和 Synchronized 的区别
Volatile本质是告诉JVM当前寄存器(工作内存)中的值是不确定的,需要从主内存中读取;Syn则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞。
volatile 仅能使用在变量级别;synchronized 则可以使用在变量、方法、和类级别的。
volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性
volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞(多个线程争抢synchronized 锁对象时,会出现阻塞)
volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化。
仅仅使用 volatile 并不能保证线程安全性。而 synchronized 则可实现线程的安全性。因为线程安全取决于原子性和可见性