在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,觉得使用这个关键字,在进行多线程并发处理的时候就能够万事大吉。java
Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制。编程
synchronized 安全
同步块你们都比较熟悉,经过 synchronized 关键字来实现,全部加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程可以用多线程
synchronized 修饰的方法 或者 代码块。并发
volatilejvm
用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile很容易被误用,用来进行原子性操做。优化
要使 volatile 变量提供理想的线程安全,必须同时知足下面两个条件:spa
实际上,这些条件代表,能够被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。 线程
第一个条件的限制使 volatile 变量不能用做线程安全计数器。虽然增量操做(x++
)看上去相似一个单独操做,实际上它是一个由读取-修改-写入操做序列组成的组合操做,必须以原子方式执行,而 volatile 不能提供必须的原子特性。实现正确的操做须要使 x
的值在操做期间保持不变,而 volatile 变量没法实现这点。(然而,若是将值调整为只从单个线程写入,那么能够忽略第一个条件。) code
大多数编程情形都会与这两个条件的其中之一冲突,使得 volatile 变量不能像 synchronized
那样广泛适用于实现线程安全。清单 1 显示了一个非线程安全的数值范围类。它包含了一个不变式 —— 下界老是小于或等于上界。
一旦一个共享变量(类的成员变量、类的静态成员变量)被 volatile 修饰以后,那么就具有了两层语义:
在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,觉得使用这个关键字,在进行多线程并发处理的时候就能够万事大吉。
Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制。
synchronized
同步块你们都比较熟悉,经过 synchronized 关键字来实现,全部加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程可以用
synchronized 修饰的方法 或者 代码块。
volatile
用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile很容易被误用,用来进行原子性操做。
要使 volatile 变量提供理想的线程安全,必须同时知足下面两个条件:
实际上,这些条件代表,能够被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。
第一个条件的限制使 volatile 变量不能用做线程安全计数器。虽然增量操做(x++
)看上去相似一个单独操做,实际上它是一个由读取-修改-写入操做序列组成的组合操做,必须以原子方式执行,而 volatile 不能提供必须的原子特性。实现正确的操做须要使 x
的值在操做期间保持不变,而 volatile 变量没法实现这点。(然而,若是将值调整为只从单个线程写入,那么能够忽略第一个条件。)
大多数编程情形都会与这两个条件的其中之一冲突,使得 volatile 变量不能像 synchronized
那样广泛适用于实现线程安全。清单 1 显示了一个非线程安全的数值范围类。它包含了一个不变式 —— 下界老是小于或等于上界。
一旦一个共享变量(类的成员变量、类的静态成员变量)被 volatile 修饰以后,那么就具有了两层语义:
1)保证了不一样线程对这个变量进行操做时的可见性,即一个线程修改了某个变量的值,这新值对其余线程来讲是
当即可见的。
2)禁止进行指令重排序。
volatile 本质是在告诉 jvm 当前变量在寄存器(工做内存)中的值是不肯定的,须要从主存中读取;
synchronized 则是锁定当前变量,只有当前线程能够访问该变量,其余线程被阻塞住。
1.volatile 仅能使用在变量级别;
synchronized 则可使用在变量、方法、和类级别的
2.volatile 仅能实现变量的修改可见性,并不能保证原子性;
synchronized 则能够保证变量的修改可见性和原子性
3.volatile 不会形成线程的阻塞;
synchronized 可能会形成线程的阻塞。
4.volatile 标记的变量不会被编译器优化;
synchronized 标记的变量能够被编译器优化