JUC-volatile详解
Posted 玩葫芦的卷心菜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC-volatile详解相关的知识,希望对你有一定的参考价值。
volatile是Java虚拟机提供的轻量级同步机制,有三大特性:保证可见性,不保证原子性,禁止指令重排列
JMM(Java内存模型)
volatile有三大特性:可见性、无原子性、禁止重排序
1、可见性:
主内存共享,每个工作线程会创建一份工作内存,操作共享变量,会进行变量拷贝进工作内存,在工作内存中修改后会写回主内存,但是其他工作线程不知道,所以可见性就是当一个工作线程修改共享变量后写回主内存,由主内存通知其他工作线程,旧值作废,重新拷贝新值
// volatile保证可见性,及时通知其他线程,主内存已被修改
private static void seeOkByVolatile() throws InterruptedException
MyData myData=new MyData();
// CountDownLatch countDownLatch=new CountDownLatch(1);
Thread t1=new Thread()
@Override
public void run()
System.out.println(currentThread().getName()+" come in");
// 暂停一段时间,让别的线程读取到数据
try
TimeUnit.SECONDS.sleep(3);
catch (InterruptedException e)
e.printStackTrace();
myData.update();
System.out.println(currentThread().getName()+" update number:"+myData.number);
// countDownLatch.countDown();
;
t1.setName("A");
t1.start();
// countDownLatch.await();
// while(Thread.activeCount()>2)
// Thread.yield();
//
// t1.join();
// System.out.println(myData.number);
// System.out.println();
// 已经读到myData.number=0了
while(myData.number==0)
System.out.println("end");
休眠3秒,是为了主线程的myData.number已经成功读取主内存
如果没有可见性就会发现子线程修改number后还是一直循环,main线程并不知道已修改
volatile就可以保证可见性
2、原子性
操作一体,要不全部成功,要不全部失败,中间不能加塞(不能有其他操作影响)或者打断,保证结果正确
MyData myData=new MyData();
CountDownLatch countDownLatch=new CountDownLatch(10);
for(int i=0;i<10;i++)
new Thread(()->
for(int j=0;j<1000;j++)
myData.incr();
myData.incrAtomic();
countDownLatch.countDown();
).start();
try
countDownLatch.await();
catch (InterruptedException e)
e.printStackTrace();
System.out.println(myData.number);
System.out.println(myData.atomicInteger);
线程进行自增,结果不一致
volatile为什么不保证原子性,是因为线程A和线程B都自增1,B写回了主内存,但是还没通知,A线程可能也写回主内存,造成了写覆盖,写丢失的情况(自增覆盖)
所以保证原子性,就需要原子类或者synchronize(但是太重了没必要,原子类更适合),原子类是通过CAS自旋来保证
3、禁止重排列
编译器会优化指令,对指令进行重排列,但在多线程环境可以会造成结果不一致的问题
其实禁止重排列,是加上了内存屏障,同时会强制刷新内存,保证了可见性
单例模式:如果只生成了一个对象就只会出现一次构造输出
class Single
private static volatile Single instance=null;
public Single()
System.out.println("Single构造器");
public static Single getInstance()
if(instance==null)
synchronized (Single.class)
if(instance==null)
instance=new Single();
// instance=new Single();
return instance;
public class DemoSingle
public static void main(String[] args)
for(int i=0;i<10;i++)
new Thread(()->
Single.getInstance();
).start();
初始化对象的三步骤:
1、分配内存空间memory
2、初始化对象instance(memory)
3、将instance指向内存空间memory(此时instance!=null)
但是如果指令重排列
1、分配内存空间memory
2、将instance指向内存空间memory(此时instance!=null)
3、初始化对象instance(memory)
第二步可能其他线程检测到instance!=null,调过同步代码块继续生成对象(当然这种情况很好,但是需要保证,加上volatile禁止重排列)
以上是关于JUC-volatile详解的主要内容,如果未能解决你的问题,请参考以下文章