volatile## 标题
volatile 是 Java 虚拟机提供的轻量级的同步机制,具有三大特性
保证可见性
禁止指令排序
不保证原子性(synchronized可以保证原子性,但效率低)
#1 JMM(Java 内存模型)
基本概念
JMM 本身是一种抽象的概念并不是真实存在,它描述的是一组规定或则规范,通过这组规范定义了程序中的访问方式。
要求 可见性 原子性 有序性
线程解锁前,必须把共享变量的值刷新回主内存
线程加锁前,必须读取主内存的最新值到自己的工作内存
加锁解锁是同一把锁
JVM 运行程序的实体是线程,而每个线程创建时 JVM 都会为其创建一个工作内存,工作内存是每个线程的私有数据区域,而 Java 内存模型中规定所有变量的储存在主内存,主内存是共享内存区域。
首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成。
内存模型图
#保证可见性
class MyData{
volatile int number = 0;
public void addT060() {
this.number = 60;
}
public void addPlusPlus() {
number++;
}
}
public class VolatileDemo {
public static void seevolatile() {
MyData myData = new MyData();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"come in");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
myData.addT060();
System.out.println(Thread.currentThread().getClass()+"update");
},"aaa").start();
while(myData.number == 0) {
}
System.out.println(Thread.currentThread().getName()+"mission success");
}
如果不加volatile 主线程不能感知到myData的变化,进入死循环,无法打印mission success。
#不保证原子性
class MyData{
volatile int number = 0;
public void addT060() {
this.number = 60;
}
public void addPlusPlus() {
number++;
}
AtomicInteger atomicInteger = new AtomicInteger();
public void addAtomic() {
atomicInteger.getAndIncrement();
}
}
/**
* 验证可见性
* @author we
*
*/
public class VolatileDemo {
public static void main(String[] args) {
MyData myData = new MyData();
for (int i = 0; i < 20; i++) {
new Thread(()->{
for(int j = 0; j < 1000; j++) {
myData.addPlusPlus();
myData.addAtomic();
}
},String.valueOf(i)).start();
}
while(Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(myData.number);
System.out.println(myData.atomicInteger);
}
}
voatile不能保证原子性,需要调用原子类。
#禁止指令重排
volatile 实现禁止指令重排序的优化,从而避免了多线程环境下程序出现乱序的现象。
由内存屏障(Memory Barrier)实现。
应用:单例模式-懒汉式
public class SingletonDemo {
private SingletonDemo() {
System.out.println("构造");
}
private static SingletonDemo singletonDemo = null;
//DCL模式 double check lock
public static SingletonDemo getsingle() {
if(singletonDemo == null) {
singletonDemo = new SingletonDemo();
}
}
}
return singletonDemo;
}
public static void main(String[] args) {
for(int i = 0; i < 10; i++) {
new Thread(()->{
SingletonDemo.getsingle();
},String.valueOf(i)).start();
}
}
}
懒汉式多线程线程不安全,会多次构造。采用DCL改进
public class SingletonDemo {
private SingletonDemo() {
System.out.println("构造");
}
private static SingletonDemo singletonDemo = null;
//DCL模式 double check lock
public static SingletonDemo getsingle() {
if(singletonDemo == null) {
synchronized (SingletonDemo.class) {
if(singletonDemo == null) {
singletonDemo = new SingletonDemo();
}
}
}
return singletonDemo;
}
public static void main(String[] args) {
for(int i = 0; i < 10; i++) {
new Thread(()->{
SingletonDemo.getsingle();
},String.valueOf(i)).start();
}
}
}
此时仍然不安全,因为指令重排
new对象伪代码
1分配内存空间
2初始化对象
3指向刚分配的内存地址,此时对象不是null
如果指令重排是1 3 2的顺序,实例未必初始化完成,造成线程安全问题。
解决方法:private static volatile SingletonDemo singletonDemo = null;
加volatile。