深入单例模式和深入理解CAS模式

Posted 艾编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入单例模式和深入理解CAS模式相关的知识,希望对你有一定的参考价值。

大家好,我是艾编程的小艾同学,当给大家分享到这里的时候,这个已经是JUC并发编程的最后几个系列,还有两篇文章就结束了,我们希望通过微信公众号做系列的课程,能够对大家有帮助,大家可以积极的参与互动,看看想要学习什么样的课程内容,我这边会接着下来协调资源,给大家更新相关系列专辑内容哦!

18、深入单例模式

深入单例模式

1、饿汉式

package com.coding.single;
public class Hungry {
private byte[] data1 = new byte[10240]; private byte[] data2 = new byte[10240]; private byte[] data3 = new byte[10240]; private byte[] data4 = new byte[10240];
// 单例模式核心思想,构造器私有! private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){ return HUNGRY; }}

2、懒汉式 DCL 双重检测锁

package com.coding.single;
public class LazyMan { private LazyMan(){ System.out.println(Thread.currentThread().getName()+"Start"); }
private volatile static LazyMan lazyMan;
public static LazyMan getInstance(){ if (lazyMan==null){ synchronized (LazyMan.class){ if (lazyMan==null){ lazyMan = new LazyMan(); // 可能存在指令重排! /* A:1 3 2 B:lazyMan = null ; 1. 分配对象的内存空间 2. 执行构造方法初始化对象 3. 设置实例对象指向刚分配的内存的地址, instance = 0xfffff; */ } } } return lazyMan; }
public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(()->{ LazyMan.getInstance(); }).start(); } }
}

3、静态内部类

package com.coding.single;
import java.util.concurrent.RecursiveTask;
public class Holder { private Holder(){
}
public static Holder getInstance(){ return InnerClass.HOLDER; }
private static class InnerClass { private static final Holder HOLDER = new Holder(); }}

反射:

package com.coding.single;
import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;
public class LazyMan {
private static boolean flag = false;
private LazyMan(){ synchronized (LazyMan.class){ if (flag==false){ flag = true; }else { throw new RuntimeException("不要试图使用反射破坏单例模式"); } } }
private volatile static LazyMan lazyMan;
public static LazyMan getInstance(){ if (lazyMan==null){ synchronized (LazyMan.class){ if (lazyMan==null){ lazyMan = new LazyMan(); // 可能存在指令重排! /* A:1 3 2 B:lazyMan = null ; 1. 分配对象的内存空间 2. 执行构造方法初始化对象 3. 设置实例对象指向刚分配的内存的地址, instance = 0xfffff; */ } } } return lazyMan; }
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//LazyMan instance1 = LazyMan.getInstance(); Constructor<LazyMan> declaredConstructors = LazyMan.class.getDeclaredConstructor(null); declaredConstructors.setAccessible(true); // 无视 private 关键字
Field flag = LazyMan.class.getDeclaredField("flag"); flag.setAccessible(true);
LazyMan instance1 = declaredConstructors.newInstance();
flag.set(instance1,false);

LazyMan instance2 = declaredConstructors.newInstance();
System.out.println(instance1); System.out.println(instance2);
}
}

4、枚举 (最安全的)

package com.coding.single;
import java.lang.reflect.Constructor;
// 枚举是一个类!EnumSingle.classpublic enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){ return INSTANCE; }
public static void main(String[] args) throws Exception { EnumSingle enumSingle2 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); // 期望的异常 throw new IllegalArgumentException("Cannot reflectively create enum objects");
// java.lang.NoSuchMethodException: com.coding.single.EnumSingle.<init>() declaredConstructor.newInstance(); }
}

深入单例模式和深入理解CAS模式

jad 反编译工具!

找到万恶之源

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.// Jad home page: http://www.kpdus.com/jad.html// Decompiler options: packimports(3) // Source File Name: EnumSingle.java
package com.coding.single;
import java.lang.reflect.Constructor;
public final class EnumSingle extends Enum{
public static EnumSingle[] values() { return (EnumSingle[])$VALUES.clone(); }
public static EnumSingle valueOf(String name){ return (EnumSingle)Enum.valueOf(com/coding/single/EnumSingle, name); }
private EnumSingle(String s, int i){ super(s, i); }
public EnumSingle getInstance(){ return INSTANCE; }
public static void main(String args[]) throws Exception{ EnumSingle enumSingle2 = INSTANCE; Constructor declaredConstructor = com/coding/single/EnumSingle.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); declaredConstructor.newInstance(null); }
public static final EnumSingle INSTANCE; private static final EnumSingle $VALUES[];
static { INSTANCE = new EnumSingle("INSTANCE", 0); $VALUES = (new EnumSingle[] { INSTANCE }); }}

再次测试

package com.coding.single;
import java.lang.reflect.Constructor;
// 枚举是一个类!EnumSingle.classpublic enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){ return INSTANCE; }
public static void main(String[] args) throws Exception { EnumSingle enumSingle2 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class); declaredConstructor.setAccessible(true); // 期望的异常 throw new IllegalArgumentException("Cannot reflectively create enum objects"); // Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects // java.lang.NoSuchMethodException: com.coding.single.EnumSingle.<init>() declaredConstructor.newInstance(); }
}

19、深入理解CAS

在互联网缩招的情下,初级程序员大量过剩,高级程序员重金难求!

CAS : 比较并交换

package com.coding.cas;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(5);
// compareAndSet 简称 CAS 比较并交换! // compareAndSet(int expect, int update) 我期望原来的值是什么,如果是,就更新
System.out.println(atomicInteger.compareAndSet(5, 2020)+"=>"+atomicInteger.get());
// 2020 System.out.println(atomicInteger.compareAndSet(2020, 1024)+"=>"+atomicInteger.get());
}}

CAS 底层原理?如果知道,谈谈你对UnSafe的理解?

getAndIncrement 分析这个+1是怎么实现的

public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1);}

深入单例模式和深入理解CAS模式

深入单例模式和深入理解CAS模式

public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;}

Unsafe 就是CAS 的核心类 !

JAVA 无法直接操作系统的底层!native

Unsafe  后门!操作特定内存中的数据! 里面的所有的所有方法 ,都可以像C的指针一样直接操作内存!

native

最后解释CAS 是什么

CAS 就是 他是一个 CPU的 并发原语!

它的功能就是判断内存中的某个位置的值,是否是预期值,如果是更新为自己指定的新值,原子性的!内存级别的,连续的

==本身就不存在了数据不一致的问题!根治!==

深入单例模式和深入理解CAS模式

汇编层面理解

Unsafe 类中的 compareAndSwapint,是一个本地方法,该方法的实现位于 unsafe.cpp 中;

深入单例模式和深入理解CAS模式

总结


CAS : 比较当前工作内存的中值和主内存的中值,如果相同,则执行操作,否则就一直比较知道值一致为止!

CAS:

内存值A:  旧的预期值 B    , 想修改为 V!

CAS的缺点:

1、循环时间长,开销大!

2、只能保证一个共享变量的原子操作!

3、ABA 问题!?狸猫换太子!

20、原子引用

ABA问题及原子引用

原子类 AtomicInteger 的ABA问题谈谈?原子更新引用知道吗?

package com.coding.cas;
import com.coding.demo02.A;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(5);
// compareAndSet 简称 CAS 比较并交换! // compareAndSet(int expect, int update) 我期望原来的值是什么,如果是,就更新
// a System.out.println(atomicInteger.compareAndSet(5, 2020)+"=>"+atomicInteger.get());
// c 偷偷的改动 System.out.println(atomicInteger.compareAndSet(2020, 2021)+"=>"+atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(2021, 5)+"=>"+atomicInteger.get());

// b System.out.println(atomicInteger.compareAndSet(5, 1024)+"=>"+atomicInteger.get());
}}

CAS 会导致 ABA的问题!

CAS算法的前提是:取出内存中某个时刻的数据,并且比较并交换!在这个时间差内有可能数据被修改!

==尽管CAS操作成功!但是不不代表这个过程就是没有问题的!==

深入单例模式和深入理解CAS模式

乐观锁!


原子引用 AtomicReference

版本号,时间戳!

版本号原子引用,类似乐观锁

package com.coding.cas;
import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicReference;import java.util.concurrent.atomic.AtomicStampedReference;
/** * AtomicReference 原子引用 * AtomicStampedReference 加了时间戳 类似于乐观锁!通过版本号 */public class CASDemo2 { static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);
public static void main(String[] args) { new Thread(()->{ //1 、 获得版本号 int stamp = atomicStampedReference.getStamp(); System.out.println("T1 stamp 01=>"+stamp);
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
atomicStampedReference.compareAndSet(100,101, atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println("T1 stamp 02=>"+atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101,100, atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println("T1 stamp 03=>"+atomicStampedReference.getStamp());
},"T1").start();
new Thread(()->{ // GIT 看到数据被动过了!
//1 、 获得版本号 int stamp = atomicStampedReference.getStamp(); System.out.println("T1 stamp 01=>"+stamp);
// 保证上面的线程先执行完毕! try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
boolean b = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1); System.out.println("T2 是否修改成功:"+b); System.out.println("T2 最新的stamp:"+stamp); System.out.println("T2 当前的最新值:"+atomicStampedReference.getReference()); },"T2").start();
}

}

解决ABA 问题:AtomicStampedReference




以上是关于深入单例模式和深入理解CAS模式的主要内容,如果未能解决你的问题,请参考以下文章

深入理解设计模式-单例模式

深入理解设计模式-单例模式(饿汉单例模式懒汉单例模式双锁单例模式)

深入理解单例模式

深入理解单例设计模式

深入理解JavaScript系列(25):设计模式之单例模式

深入理解 sync.Once:单例模式的绝佳选择