逐梦少年,看你能不能发现宝藏( 九 )


可以利用关键字解决,因为的另外一个作用就是禁止重排序的优化 。
23.你在哪些地方用到过?
? 1.单例模式DCL代码
? DCL(双端检索)机制不一定线程安全,原因是有指令重排序的存在,加入可以禁止指令重排 。
? 原因在于某一个线程执行到第一次检测,读取到的不为null时,的应用对象可能没有完成初始化 。
= new (),可以分为以下3步完成(伪代码)
//下面是名副其实
= ();//1.分配对象内存空间
()//2.初始化对象
= ; //设置指向刚分配的内存地址,此时!= null;
步骤2和步骤3不存在数据依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中没有改变,因此这种重排优化时;
//下面是有名无实
= ();//1.分配对象内存空间
= ; //设置指向刚分配的内存地址,此时!= null;但是对象没有初始化完成 。
()//2.初始化对象
但是指令重排只会保证串行语义的执行一致性(单线程),但是并不关心多线程间的语义一致性 。
所以当一条线程访问不为null时,由于实例未必已初始化完成,也就造成了线程安全问题 。
? 2.单例模式分析
//单机版下的单例模式 。public class SingletonDemo {private static volatile SingletonDemo instance = null;private SingletonDemo() {System.out.println(Thread.currentThread().getName()+"\t 我是构造方法SingletonDemo()");}//DCL模式(Double Check Lock 双端检索机制,就是在加锁前后进行判断 。推荐使用,这样可以不用锁方法 。)//public static synchronized SingletonDemo getInstance() {public staticSingletonDemo getInstance() {if (instance == null) {synchronized (SingletonDemo.class) {if (instance == null) {instance = new SingletonDemo();}}}return instance;}public static void main(String[] args) {//单线程(main线程的操作动作 。。。。。。。。。。。)/*System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());System.out.println("========================================================");*/for (int i = 0; i < 10; i++) {new Thread(()->{SingletonDemo.getInstance();}).start();}}}
24.CAS您知道么?讲一讲,为什么要用CAS而不是?CAS底层原理?如果知道,谈谈你对的理解
CAS的全程为-And-Swap,它是一条CPU并发原语 。
它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的 。
CAS并发原语体现在JAVA语言中就是.类中的哥哥方法 。调用类中的CAS方法,JVM会帮助我们实现CAS汇编指令 。这是一种完全依赖于硬件的功能 。通过它实现了原子操作 。再次强调,由于CAS是一种系统原语,原语属于从做系统用语范畴,是由若干条指令组成的 。用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题 。意思就是线程安全 。
/*** Atomically decrements by one the current value.** @return the previous value*/public final int getAndDecrement() {return unsafe.getAndAddInt(this, valueOffset, -1);}
内存偏移地址就好比是一个坐标
假设线程A和线程B两个线程同时执行操作(分别在不同的Cpu上);
1.里面的value原始值为3,即主内存中的value为3,根据JMM模型,线程A和线程B各自持有一份价值为3的valie的副本分别到各自的工作内存 。
2.线程A通过(var 1,var 2)拿到value值3,这时线程A被挂起 。
3.线程B也通过(var 1,var 2)方法获取到value值3,此时刚好线程B没有被挂起并执行方法,比较内存值也为3,成功修改内存值为4,线程B打完收工,一切OK 。