1.1.volatile是一种同步机制,比synchronized或Lock更轻量级,因为使用volatile并不会发生线程“上下文切换”等开销很大的行为,volatile关键字只是把被修
饰的变量修改后刷新到“主内存”中;
1.2.如果一个变量被volatile修饰,那么JVM就知道这个变量可能会被并发修改;
1.3.volatile属于轻量级同步机制,做不到像synchronized那样的原子保护,其仅在有限的场景下使用;
如果一个共享变量自始至终只被各个线程赋值,而没有其它操作(运算或取反等操作),那么就可以使用volatile代替synchronized或者“原子变量”,因为赋值本身是原子性的,而volatile又保证了可见性所以是线程安全的;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 演示volatile适用于纯赋值场景用于保证线程安全
*/
public class UseVolatile implements Runnable{
volatile boolean flag = false;
AtomicInteger realA = new AtomicInteger();
@Override
public void run() {
for (int i = 0; i <10000 ; i++) {
setFlag();
realA.incrementAndGet(); // 累加
}
}
private void setFlag() {
flag = true; // 此种写法无线程安全问题,纯赋值
// flag = !flag; // 若这样写则有线程安全问题,因为此处的赋值依赖于之前的变量值;
}
public static void main(String[] args) throws InterruptedException {
UseVolatile r = new UseVolatile();
Thread thread1 = new Thread(r);
Thread thread2 = new Thread(r);
thread1.start();
thread2.start();
// 主线程等待子线程执行完
thread1.join();
thread2.join();
System.out.println("volatile适用于纯赋值: " + r.flag);
System.out.println("执行次数: " + r.realA); // 20000
}
}
伪代码示例
/* 模拟volatile作为“触发器”多线程初始化完成后去执行任务 */
Map configOptions;
cahr[] configText;
volatile boolean initialized = false; // 此字段被volatile修饰,用于判断是否完成初始化
// 线程一
configOptions = new HashMap();
configText = readConfigFile(fileName);
processConfigOptions(configText,configOptions)
initialized = true; // initialized被volatile修饰具有禁止指令重排性,当initialized=true;代表上面configOptions、configText等参数或方法已经完成了初始化;
// 线程二
while(!initialized){ // 此时的initialized作为触发器(因为initialized被volatile修饰具有可见性所以线程一的修改线程二能看见,只有当线程一完成了初始化,线程二才会去具体执行的业务,不然一直在循环中休眠等待初始化完成...)
Thread.sleep(1000);
}
// 线程一初始化完成线程二去做其它业务..use config make todo...
import java.util.concurrent.atomic.AtomicInteger;
/**
* 演示volatile不适用于a++场景
*/
public class NoVolatile implements Runnable{
volatile int a;
// 变量realA主要用于与变量a做对比
AtomicInteger realA = new AtomicInteger();
@Override
public void run() {
for (int i = 0; i <10000 ; i++) {
a++;
realA.incrementAndGet(); // 累加
}
}
public static void main(String[] args) throws InterruptedException {
NoVolatile r = new NoVolatile();
Thread thread1 = new Thread(r);
Thread thread2 = new Thread(r);
thread1.start();
thread2.start();
// 主线程等待子线程执行完
thread1.join();
thread2.join();
System.out.println("使用volatile修饰变量a的最终累加值为: " + r.a);
System.out.println("最终累加正确值应为: " + r.realA); // 20000
}
}
读一个volatile修饰的变量前需先使这个变量在线程内部相应的缓存失效(内部缓存:即每个线程独占的缓存),这样就必须去主内存中读取最新值(主内存:即所有线程共享的内存),当写一个volatile变量时会立即刷入到主内存中;
volatile可以看做是一个轻量级的synchronized,如果一个共享变量自始至终都是被各个线程赋值,而没有做其它操作(运算或取反等操作),那么就可以使
用volatile代替synchronized,因为赋值本身是原子性的,而volatile又保证了可见性所以是线程安全的;
5.1.volatile适用于一个共享变量自始至终只被各个线程赋值,而没有其它操作(运算或取反等操作),那么就可以使用volatile代替synchronized或者“原子变
量”,因为赋值本身是原子性的,而volatile又保证了可见性所以是线程安全的;或者也可以使用volatile作为触发器实现轻量数据同步;
5.2.volatile属性的读写操作都是无锁的,它不能替代synchronized因为它没有提供原子性和互斥性,因为无锁所以不需要花时间在获取锁和释放锁上所以
它是轻量级的;
5.3.volatile只能修饰属性,使用volatile修饰的属性不会被编译器“指令重排”;
5.4.volatile提供了可见性,任何一个线程对其修饰的属性进行修改,其它线程会立马可见,volatile属性不会被线程缓存,始终从主内存中取;
5.5.volatile提供了happens-before保证;
5.6.volatile可以j使long和double的赋值是原子的(long和double本身赋值并不能保证是原子性的);