【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禁止重排列)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值