2019大厂面试题(volatile+CAS)

一、Volatile是什么

volatile是java虚拟机提供的轻量级的同步机制

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排(保证有序性)

JMM(Java内存模型)

1.可见性
2.原子性
3.有序性

1.1保证可见性

在这里插入图片描述
在这里插入图片描述
模拟volatile可见性案例,如下

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.entity;

import java.util.concurrent.TimeUnit;

/**
 * @author yangjikang
 * @date 2019/6/5 17:26
 * @description 模拟volatile可见性案例
 * @modified By yangjikang
 */
public class Sync {
    //模拟volatile的可见性
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        //A线程更改number的值
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t线程 coming");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            mt.setNumber();
            System.out.println(Thread.currentThread().getName() + "线程 update value :"+mt.number);
        },"A1").start();
        //
        while (mt.number == 0){
            //直到main方法读取到numer != 0则跳出循坏
        }
        System.out.println(Thread.currentThread().getName() + "获取到当前的number:"+mt.number);
    }
}
class MyThread {
    /*volatile*/ int number = 0;

    public void setNumber() {
        this.number = 60;
    }
}

1.2不保证原子性

模拟volatile不保证原子性案例,如下

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.entity;

/**
 * @author yangjikang
 * @date 2019/6/5 17:26
 * @description 模拟volatile不保证原子性案例
 * @modified By yangjikang
 */
public class Sync {
    //模拟volatile不保证原子性
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        //模拟20个线程调用addNumber方法给number++.
        for (int i = 0; i < 40 ; i++) {
            new Thread(() -> {
                for (int j = 0; j <10000; j++) {
                    mt.addNumber();
                }
            },String.valueOf(i)).start();
        }
        //等待上面20个线程都计算完成,再用main线程获取修改之后的值
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "获取到当前的number:"+mt.number);
    }
}
class MyThread {
    volatile int number = 0;

    public void setNumber() {
        this.number = 60;
    }
    public void addNumber() {
       number ++;
    }
}

编译后的class文件,字节码的原理图
在这里插入图片描述

1.2.1解决volatile不保证原子性的问题

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.jikang.entity;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author yangjikang
 * @date 2019/6/5 17:26
 * @description 模拟解决volatile不保证原子性的问题
 * @modified By yangjikang
 */
public class Sych {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        //模拟20个线程调用addNumber方法给number++.
        for (int i = 0; i < 40 ; i++) {
            new Thread(() -> {
                for (int j = 0; j <10000; j++) {
                    mt.addNumber();
                    mt.addAtomicNumber();
                }
            },String.valueOf(i)).start();
        }
        //等待上面20个线程都计算完成,再用main线程获取修改之后的值
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "获取到当前的number:"+mt.number);
        System.out.println(Thread.currentThread().getName() + "获取到Atomic方法的number:"+mt.atomicInteger);
    }
}
class MyThread {
    volatile int number = 0;

    public void setNumber() {
        this.number = 60;
    }
    public void addNumber() {
       number ++;
    }
    //解决volatile不保证原子性
    AtomicInteger atomicInteger = new AtomicInteger();
    public void addAtomicNumber() {
        //原子性的 i++
        atomicInteger.getAndIncrement();
    }
}

效果图
在这里插入图片描述

1.3 指令重排

1.3.1 指令重排一

在这里插入图片描述
在这里插入图片描述

1.3.2 指令重排二

这种效果可能的情况只有99.9%,这里就不实验了,可能试一万次都不一定有效果
在这里插入图片描述

1.4 单例模式volatile分析

在这里插入图片描述
解析图
在这里插入图片描述

二、CAS是什么

一系列问题
在这里插入图片描述
CAS就是一个自旋锁
CAS :比较并交换
CAS ==> compareAndSwap,它是一条CPU并发原语
在这里插入图片描述
在这里插入图片描述

2.1 CAS底层原理-上

CAS ==> compareAndSwap,它是一条CPU并发原语
在这里插入图片描述
UnSafe类
在这里插入图片描述
接口调用
在这里插入图片描述
在这里插入图片描述

2.2 CAS的缺点

1.循环时间长开销大
2.只保证一个共享变量的原子操作
3.引出来ABA问题???

1.1 循环时间长开销大

在这里插入图片描述

1.2 只保证一个共享变量的原子操作

在这里插入图片描述

3 引出来ABA问题???

ABA问题的图解,CAS只管开头和结尾不管过程,ABA问题就是这个过程会被修改
在这里插入图片描述
在这里插入图片描述

1.1 理解AtomicReference原子引用

在这里插入图片描述
在这里插入图片描述

1.2 AtomicStampedReference时间戳(版本号)的原子引用

1.2.1 ABA问题的产生代码

这里用的是一个AtomicReference<>

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @author yangjikang
 * @date 2019/6/12 13:40
 * @description ABA问题产生
 * @modified By yangjikang
 */
public class TestAtomicStampedReference {
    //普通的原子引用
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);

    public static void main(String[] args) {
        System.out.println("=================下面是ABA问题的模拟场景===================");

        //T1线程执行ABA操作
        new Thread(()->{
            atomicReference.compareAndSet(100,101);
            atomicReference.compareAndSet(101,100);
        },"T1").start();

        //T2线程正常操作数据
        new Thread(()->{
            //暂停2秒,保证T1线程完成一次ABA操作
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean result = atomicReference.compareAndSet(100, 2019);
            System.out.println(Thread.currentThread().getName()+"线程更改成功没有:"+result+",当前数据是:"+atomicReference.get());
        },"T2").start();
    }
}

控制台截图
在这里插入图片描述

1.2.2 ABA问题的解决代码

这里用是的一个AtomicStampedReference<>

/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @author yangjikang
 * @date 2019/6/12 13:40
 * @description ABA问题解决
 * @modified By yangjikang
 */
public class TestAtomicStampedReference {
    //带时间戳(版本号)的原子引用
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);

    public static void main(String[] args) {

        System.out.println("=================下面是ABA问题的解决方案===================");

        //T3线程执行ABA操作
        new Thread(()->{

            //获取当前原子版本号
            int stamp = atomicStampedReference.getStamp();
            //暂停1秒,保证T3线程完成一次ABA操作
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean oneReference = atomicStampedReference.compareAndSet(100, 101,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName()+"线程更改成功没有:"+oneReference+"第1次更改之后的版本号"+atomicStampedReference.getStamp());
            boolean TwoReference = atomicStampedReference.compareAndSet(101, 100,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName()+"线程更改成功没有:"+TwoReference+"第2次更改之后的版本号"+atomicStampedReference.getStamp());
        },"T3").start();

        //T4线程正常操作数据
        new Thread(()->{
            //获取当前原子版本号
            int stamp = atomicStampedReference.getStamp();
            //暂停3秒,保证T3线程完成一次ABA操作
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean result = atomicStampedReference.compareAndSet(100, 2019,
                    stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName()+"线程更改成功没有:"+result+",当前数据是:"+
                    atomicStampedReference.getReference()+",当前版本号是"+atomicStampedReference.getStamp());
        },"T4").start();
    }
}

控制台截图
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值