共性:volatile与synchronized都用于保证多线程中数据的安全性
1、Java语言为了解决并发编程中存在的原子性、可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如 synchronized、volatile、final、concurren 包等。
2、synchronized 通过加锁的方式,使得其在需要原子性、可见性和有序性这三种特性的时候都可以作为其中一种解决方案,看起来是“万能”的。的确,大部分并发控制操作都能使用 synchronized 来完成。
3、volatile 通过在 volatile 变量的操作前后插入内存屏障的方式,保证了变量在并发场景下的可见性和有序性。
4、volatile 关键字是无法保证原子性的,而 synchronized 通过 monitorenter 和monitorexit 两个指令,可以保证被 synchronized 修饰的代码在同一时间只能被一个线程访问,即可保证不会出现CPU时间片在多个线程间切换,即可保证原子性。
------什么是可见性和原子性?
【1】可见性
说的是一个线程如果更改了某个变量的值,其他线程能够立刻知道这个变量更改后的值。
【2】原子性
一个操作要么全做,要么全不做,就像不可分割的原子一样。
【3】有序性
为了提高执行效率,java 中的编译器和处理器可以对指令进行重新排序,重新排序会影响多线程并发的正确性,有序性就是要保证不进行重新排序(保证线程操作的执行顺序)。
一.Volatile
Volatile可以看做是一个轻量级的synchronized,它可以在多线程并发的情况下保证变量的“可见性”,什么是可见性?
-------就是在一个线程的工作内存中修改了该变量的值,该变量的值能立即回显到主内存中,从而保证所有的线程看到这个变量的值是最新的。
所以在处理同步问题上它大显作用,而且它的开销比synchronized小、使用成本更低。volatile用于修饰变量。
二.Synchronized
synchronized 叫做同步锁,是Lock的一个简化版本,由于是简化版本,那么性能肯定是不如Lock的,不过它操作起来方便,只需要用它修饰一个方法或者一段代码块,那么这段代码就是同步的了,所有线程对这块区域的访问必须先持有锁才能进入,否则,则拦截在外面,等待正在持有锁的线程处理完毕再获取锁进入,正因为它基于这种阻塞的策略,所以它的性能不太好,但是由于操作上的优势,只需要简单的声明一下即可,而且被它声明的代码块也是具有操作的原子性。
因为synchronized 保证了在同一时刻,只能有一个线程执行同步代码块,所以执行同步代码块的时候相当于是单线程操作了,那么线程的可见性、原子性、有序性(线程之间的执行顺序)它都能保证了。
三.总结
- 1.volatile 仅能使用在变量级别,synchronized 则可以使用在变量、方法、类级别、代码块上。
- 2.volatile 仅仅能实现变量修改可见性,并不能保证原子性,volatile 表示变量在 CPU 的寄存器中是不确定的,必须从主存中读取,synchronized 可以实现变量的修改可见性和原子性。
- 3.volatile 不会造成线程阻塞,synchronized 可能会造成线程阻塞。
- 4.volatile 标记的变量不会被编译器优化,synchronized 标记的变量可以被编译器优化。
- 5.volatile 修饰的变量,jvm 每次都从主内存中读取,而不会从寄存器(工作内存)中读取。synchronized 表示只有一个线程可以获取作用 对象 的锁,执行代码,阻塞其他线程。
- volatie 仅能实现变量的可见性,无法保证变量操作的原子性。而synchronized可以实现变量的可见性与原子性。