深入单例模式和深入理解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.class
public 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();
}
}
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.class
public 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);
}
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的 并发原语!
它的功能就是判断内存中的某个位置的值,是否是预期值,如果是更新为自己指定的新值,原子性的!内存级别的,连续的
==本身就不存在了数据不一致的问题!根治!==
汇编层面理解
Unsafe 类中的 compareAndSwapint,是一个本地方法,该方法的实现位于 unsafe.cpp 中;
总结
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操作成功!但是不不代表这个过程就是没有问题的!==
乐观锁!
原子引用 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模式的主要内容,如果未能解决你的问题,请参考以下文章
深入理解设计模式-单例模式(饿汉单例模式懒汉单例模式双锁单例模式)