18彻底玩转 单例模式

饿汉式 DCL懒汉模式 探究!

饿汉式

 package com.kuang.single;
 //饿汉式单例
 //单例模式重要思想是构造器私有
 public class Hungry {
 ​
     //可能会浪费空间
     private byte[] data1 = new byte[1024*1024];
     private byte[] data2 = new byte[1024*1024];
     private byte[] data3 = new byte[1024*1024];
     private byte[] data4 = new byte[1024*1024];
 ​
     private  Hungry(){
 ​
    }
     private final static Hungry HUNGRY = new Hungry();
 ​
     public static Hungry getInstance(){
         return HUNGRY;
    }
 }
 ​

DCL懒汉式

 package com.kuang.single;
 ​
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 ​
 //懒汉式单例
 public class LayzMan {
     private static boolean qinjiang = false;
 ​
     private LayzMan() {
 ​
             synchronized (LayzMan.class){
                 if (qinjiang == false){
                     qinjiang = true;
                }
                 else {
                     throw new RuntimeException("不要试图使用反射破坏异常");
                }
 ​
            }
        }
 ​
 ​
 ​
 ​
     private volatile static LayzMan layzMan;
 ​
     public static LayzMan getInstance() {
         //加锁
         //双重检测锁模式的 懒汉式单例 DCL懒汉模式
         if (layzMan == null) {
             synchronized (LayzMan.class) {//锁class只有一个
                 if (layzMan == null) {
                     layzMan = new LayzMan();//不是一个原子性操作
 ​
                }
            }
        }
 ​
         return layzMan;//加了线程B后 此时layzMan还没有完成构造
    }
     /**
      *1.分配内存空间
      * 2.执行构造方法,初始化对象
      * 3.把这个对象指向这个空间
      *
      * 假设原本希望执行123
      * 真实可能执行132 若只有单线程A可以执行 若再加了一个线程B会出现问题 了线程B后 此时layzMan还没有完成构造
      */
     //反射
 ​
     public static void main(String[] args) throws Exception {
      // LayzMan instance = LayzMan.getInstance();
 ​
         Field qinjiang = LayzMan.class.getDeclaredField("qinjiang");
         qinjiang.setAccessible(true);
         Constructor<LayzMan> declaredConstructor = LayzMan.class.getDeclaredConstructor(null);
         declaredConstructor.setAccessible(true);
         LayzMan instance = declaredConstructor.newInstance();
         qinjiang.set(instance,false);
         LayzMan instance2 = declaredConstructor.newInstance();
 ​
         System.out.println(instance);
         System.out.println(instance2);
    }
 ​
 }

静态内部类

 package com.kuang.single;
 //静态内部类 在一个类里面再写一个静态类
 public class Holder {
     private Holder(){
 ​
    }
 ​
     public static Holder getInstance(){
         return InnerClass.HOLDER;
    }
 ​
     public static class InnerClass{
         private static final Holder HOLDER = new Holder();
 ​
    }
 }
 ​

单例不安全,存在反射

枚举enum

 package com.kuang.single;
 ​
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 ​
 //enum (枚举)是个什么? 枚举本身也是一个Class类
 //反射不能破坏枚举的单例Cannot reflectively create enum objects
 public enum EnumSingle {
 ​
 ​
     INSTANCE;
     public EnumSingle getInstance(){
         return INSTANCE;
    }
 }
 ​
 class Test{
     public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
         EnumSingle instance1 = EnumSingle.INSTANCE;
         Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
         declaredConstructor.setAccessible(true);
         EnumSingle instance2 = declaredConstructor.newInstance();
         System.out.println(instance1);
         System.out.println(instance2);
 ​
         //NoSuchMethodException: com.kuang.single.EnumSingle.<init>()这个类里面没有空参构造器
 ​
 ​
    }
 }

枚举为有参构造 两个参数

把一个class文件反编译为java的操作:

枚举类型的最终反编译源码:

 // 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.kuang.single;
 ​
 ​
 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/kuang/single/EnumSingle, name);
    }
 ​
     private EnumSingle(String s, int i)
    {
         super(s, i);
    }
 ​
     public EnumSingle getInstance()
    {
         return INSTANCE;
    }
 ​
     public static final EnumSingle INSTANCE;
     private static final EnumSingle $VALUES[];
 ​
     static
    {
         INSTANCE = new EnumSingle("INSTANCE", 0);
         $VALUES = (new EnumSingle[] {
             INSTANCE
        });
    }
 }
 ​

19深入理解CAS

什么是CAS

Unsafe类

 package com.cas;
 ​
 import java.util.concurrent.atomic.AtomicInteger;
 ​
 public class CASDemo {
     //CAS compareAndSet比较并交换
 ​
 ​
     public static void main(String[] args) {
         AtomicInteger atomicInteger = new AtomicInteger(2021);
 ​
         //expect:期望 update:更新
 ​
         // public final boolean compareAndSet(int expect, int update)
 ​
         //如果我期望的值达到了,那么就更新,否则就不更新,CAS是CPU的并发原语
         System.out.println(atomicInteger.compareAndSet(2021, 2022));
         System.out.println(atomicInteger.get());
         atomicInteger.getAndIncrement();//
 ​
         System.out.println(atomicInteger.compareAndSet(2021, 2022));
         System.out.println(atomicInteger.get());
 ​
    }
 }
 ​

CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!

缺点:

1.循环会耗时

2.一次性只能保证一个共享变量的原子性

3.存在ABA问题

CAS:ABA问题:(狸猫换太子)

 ​
 package com.cas;
 ​
 import java.util.concurrent.atomic.AtomicInteger;
 ​
 public class CASDemo {
     //CAS compareAndSet比较并交换
 ​
 ​
     public static void main(String[] args) {
         AtomicInteger atomicInteger = new AtomicInteger(2021);
         
 ​
         //expect:期望 update:更新
 ​
         // public final boolean compareAndSet(int expect, int update)
 ​
         //如果我期望的值达到了,那么就更新,否则就不更新,CAS是CPU的并发原语
         //====================捣乱的线程=========================
         System.out.println(atomicInteger.compareAndSet(2021, 2022));
         System.out.println(atomicInteger.get());
 ​
 ​
         System.out.println(atomicInteger.compareAndSet(2022, 2021));
         System.out.println(atomicInteger.get());
 ​
         //===========================期望的线程=====================
         System.out.println(atomicInteger.compareAndSet(2021, 6666));
         System.out.println(atomicInteger.get());
 ​
    }
 }
 ​

20.原子引用

带版本号的原子操作!

解决ABA问题,引入原子引用!对应的思想是乐观锁

 package com.cas;
 ​
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.atomic.AtomicStampedReference;
 ​
 public class CASDemo {
     //CAS compareAndSet比较并交换
     // AtomicStampedReference注意,如果泛型是一个包装类,注意对象的引用问题
 ​
     //正常在业务操作,这里面比较的都是一个个对象
     static   AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1,1);
 ​
 ​
 ​
     public static void main(String[] args) {
         //AtomicInteger atomicInteger = new AtomicInteger(2021);
 ​
 ​
         new Thread(()->{
             int stamp = atomicStampedReference.getStamp();//获得版本号
             System.out.println("a1=>"+stamp);
             try {
                 TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
             //拿到最新的版本号,拿到之后再把这个值加一
           atomicStampedReference.compareAndSet(1, 2,
                     atomicStampedReference.getStamp(),
                     atomicStampedReference.getStamp() + 1);
 ​
             System.out.println("a2=>"+atomicStampedReference.getStamp());
             System.out.println(atomicStampedReference.compareAndSet(2, 1,
                     atomicStampedReference.getStamp(),
                     atomicStampedReference.getStamp() + 1));
             //获取最新的版本号
             System.out.println("a3=>"+atomicStampedReference.getStamp());
 ​
        },"a").start();
         new Thread(()->{
             int stamp = atomicStampedReference.getStamp();//获得版本号
             //乐观锁的原理相同
             System.out.println("b1=>"+stamp);
             try {
                 TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
             System.out.println(atomicStampedReference.compareAndSet(1, 6, stamp, stamp + 1));
             System.out.println("b2=>"+atomicStampedReference.getStamp());
 ​
 ​
        },"b").start();
 ​
 ​
 ​
 ​
    }
 }

注意

Integer使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new 一定会创建新的对象分配新的内存空间;

21.各种锁的理解

1.公平锁,非公平锁

公平锁:非常公平,不能够插队,线程必须先来后到!

非公平锁:非常不公平,可以插队(默认都是非公平的)

   public ReentrantLock() {
         sync = new NonfairSync();
    }
 ​
 转换为非公平锁
 public ReentrantLock(boolean fair) {
         sync = fair ? new FairSync() : new NonfairSync();
    }

2.可重入锁

可重入锁(递归锁)

Synchronized

 package com.kuang.lock;
 //Synchronized默认是非公平的
 public class Demo01 {
     public static void main(String[] args) {
         Phone phone = new Phone();
         new Thread(()->{
             phone.sms();
        },"A").start();
         new Thread(()->{
             phone.sms();
        },"B").start();
 ​
    }
 }
 class Phone{
     //synchronized只有一把锁
     public synchronized void sms(){
         System.out.println(Thread.currentThread().getName() + "sms");
         call();//这里也有锁
 ​
    }
     public synchronized void call(){
         System.out.println(Thread.currentThread().getName() + "call");
 ​
    }
 }

Lock锁

 package com.kuang.lock;
 ​
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 ​
 public class Demo02 {
     public static void main(String[] args) {
         Phone2 phone = new Phone2();
         new Thread(()->{
             phone.sms();
        },"A").start();
         new Thread(()->{
             phone.sms();
        },"B").start();
 ​
    }
 }
 class Phone2{
     Lock lock = new ReentrantLock();
     public  void sms(){
         lock.lock();//细节问题:两把钥匙 第一把钥匙开外面的锁,另外一把开里面的锁
         //lock.lock()负责解lock.unlock();
         //lock 锁必须配对,否则就会死在里面
 ​
 ​
         try {
             System.out.println(Thread.currentThread().getName() + "sms");
             call();//这里也有锁
        } catch (Exception e) {
             e.printStackTrace();
        } finally {
             lock.unlock();
        }
 ​
 ​
    }
     public  void call(){
         lock.lock();
 ​
         try {
             System.out.println(Thread.currentThread().getName() + "call");
        } catch (Exception e) {
             e.printStackTrace();
        } finally {
             lock.unlock();
        }
 ​
    }
 }
 ​

3.自旋锁

spinlock

不断的尝试直到成功为止!

自定义锁来测试

 package com.kuang.lock;
 ​
 import java.util.concurrent.atomic.AtomicReference;
 ​
 /**
  * 自旋锁
  */
 public class SpinlockDemo {
     //int 类型 默认为0
     //Thread引用类型 若为空认为null
     AtomicReference<Thread> atomicReference = new AtomicReference();
 ​
 ​
 ​
     //加锁
     public void myLock(){
         Thread thread = Thread.currentThread();
         System.out.println(Thread.currentThread().getName() + "==> mylock");
          //若为空 把线程丢进去 进行无限循环
         //自旋锁
         while (!atomicReference.compareAndSet(null,thread)){
 ​
 ​
        }
 ​
 ​
    }
 ​
 ​
     //解锁
     public void myUnLock(){
         Thread thread = Thread.currentThread();
         System.out.println(Thread.currentThread().getName() + "==> myUnlock");
         //若为空 把线程丢进去 进行无限循环
         //自旋锁
       atomicReference.compareAndSet(thread,null);
 ​
 ​
    }
 }
 ​

测试

 package com.kuang.lock;
 ​
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
 ​
 public class TestSpinLock {
     public static void main(String[] args) throws InterruptedException {
         //ReentrantLock reentrantLock = new ReentrantLock();
       // reentrantLock.lock();
        // reentrantLock.unlock();
 ​
         //底层使用的自旋锁CAS
         SpinlockDemo lock = new SpinlockDemo();
 ​
         new Thread(()->{
             lock.myLock();
             try {
                 TimeUnit.SECONDS.sleep(3);
 ​
            } catch (Exception e) {
                 e.printStackTrace();
            } finally {
                 lock.myUnLock();
            }
 ​
        },"T1").start();
 ​
         //延迟保证T1先获得锁
         //T1解锁之后才会释放 T2才有机会进去拿到锁并且把它解锁掉
         TimeUnit.SECONDS.sleep(1);
         new Thread(()->{
             lock.myLock();
             try {
                 TimeUnit.SECONDS.sleep(3);
 ​
            } catch (Exception e) {
                 e.printStackTrace();
            } finally {
                 lock.myUnLock();
            }
 ​
        },"T2").start();
 ​
 ​
 ​
    }
 }
 ​

结果:

T1==> mylock T2==> mylock T1==> myUnlock T2==> myUnlock

Process finished with exit code 0

4.死锁

死锁测试,怎么排除死锁

死锁:

 package com.kuang.lock;
 ​
 import lombok.SneakyThrows;
 ​
 import java.util.concurrent.TimeUnit;
 ​
 public class DeadLockDemo {
     public static void main(String[] args) {
         String lockA = "lockA";
         String lockB = "lockB";
 ​
 ​
         new Thread(new MyThread(lockA,lockB),"T1").start();
         new Thread(new MyThread(lockB,lockA),"T2").start();
 ​
    }
 }
 class MyThread implements Runnable{
 ​
 ​
     private String lockA;
     private String lockB;
 ​
    public MyThread(String lockA, String lockB) {
         this.lockA = lockA;
         this.lockB = lockB;
    }
 ​
     @SneakyThrows
     @Override
     public void run(){
        synchronized (lockA){
            //A想拿B
            System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get"+lockB);
 ​
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
 ​
            synchronized (lockB){
                //B想拿A
                System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get"+lockB);
 ​
            }
        }
 ​
    }
 ​
 }

解决问题

1.使用jps -l定位进程号

2.使用jstack进程号查看进程信息(查看怎么死锁的)

面试或者工作中:排查问题:

1.日志

2.查看堆栈信息

第47天打卡学习(单例模式 深入了解CAS 原子引用 各种锁的理解)的更多相关文章

  1. Python学习路线【对标大厂Python工程师的招聘要求,并推荐优质免费资源】打卡学习不迷茫

    您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦. 本文要点:从Python爬虫工程师的招聘要求出发制定学习路线,同时还推荐免费优质的学习资源. 打卡学习不迷茫. 干货满满,建议收藏,需要用到时常看 ...

  2. 【第一期百题计划进行中,快来打卡学习】吃透java、细化到知识点的练习题及笔试题,助你轻松搞定java

    [快来免费打卡学习]参与方式 本期百题计划开始时间:2022-02-09,今日打卡题已在文中标红. 0.本文文末评论区打卡,需要登录才可以打卡以及查看其他人的打卡记录 1.以下练习题,请用对应的知识点 ...

  3. selenium 学习之路开始了,一遍搬一遍理解学习,加油!!!

    selenium 学习之路开始了,一遍搬一遍理解学习,加油!!!

  4. Java并发包源码学习系列:基于CAS非阻塞并发队列ConcurrentLinkedQueue源码解析

    目录 非阻塞并发队列ConcurrentLinkedQueue概述 结构组成 基本不变式 head的不变式与可变式 tail的不变式与可变式 offer操作 源码解析 图解offer操作 JDK1.6 ...

  5. android学习笔记47——读写SD卡上的文件

    读写SD卡上的文件 通过Context的openFileInput.openFileOutput来打开文件输入流.输出流时,程序打开的都是应用程序的数据文件夹里的文件,其存储的文件大小可能都比较有限- ...

  6. ID卡学习笔记

    前言: 我也来篇关于当时学习ID卡的笔记.前段时间小区装门禁.一个钮扣型的ID卡就要30块.非常黑心.因为其ID卡的成本也就是1块钱以下.因此我也加入到这方面的研究.用来模拟ID卡的T5557卡成本2 ...

  7. (@WhiteTaken)设计模式学习——单例模式

    单例模式,个人理解就是,使用了这个模式,可以保证一个类只生成唯一的实例对象.就是在整个程序中,这个类只存在一个实例对象. GoF对单例模式的定义:保证一个类,只有一个实例存在,同时提供能对该实例加以访 ...

  8. java设计模式学习 ----- 单例模式(Singleton)

    单例模式(Singleton) 单例对象(Singleton)是一种经常使用的设计模式. 在Java应用中,单例对象能保证在一个JVM中,该对象仅仅有一个实例存在.单例模式也分三种:懒汉式单例.饿汉式 ...

  9. 从学习“单例模式”学到的Java知识:双重检查锁和延迟初始化

    一切真是有缘,上午刚刚看完单例模式,还在为其中的代码块同步而兴奋,下午就遇见这篇文章:双重检查锁定与延迟初始化.我一看,文章开头语出惊人,说这是一种错误的优化,我说,难道上午学的东西下午就过时了吗?仔 ...

随机推荐

  1. Docker下使用centos无法使用systemctl怎么办

    提交正在使用的容器: docker commit [ContainerId] 提交停止正在运行无法使用Systemctl的容器: docker stop [ContainerId] 删除这个容器(可选 ...

  2. 2018-2019 ACM-ICPC, NEERC, Southern Subregional Contest, Qualification Stage(11/12)

    2018-2019 ACM-ICPC, NEERC, Southern Subregional Contest, Qualification Stage A. Coffee Break 排序之后优先队 ...

  3. Codeforces #624 div3 C

    You want to perform the combo on your opponent in one popular fighting game. The combo is the string ...

  4. JSR-303 实现参数校验

    参考 1. 什么是JSR-303 JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是Hibernate Validator. 此实现与 Hib ...

  5. 【史上最全】Hadoop 核心 - HDFS 分布式文件系统详解(上万字建议收藏)

    1. HDFS概述 Hadoop 分布式系统框架中,首要的基础功能就是文件系统,在 Hadoop 中使用 FileSystem 这个抽象类来表示我们的文件系统,这个抽象类下面有很多子实现类,究竟使用哪 ...

  6. 通过js正则表达式实例学习正则表达式基本语法

    正则表达式又叫规则表达式,一般用来检查字符串中是否有与规则相匹配的子串,达到可以对匹配的子串进行提取.删除.替换等操作的目的.先了解有哪些方法可以使用正则对字符串来实现这些操作: RegExpObje ...

  7. 1009E Intercity Travelling 【数学期望】

    题目:戳这里 题意:从0走到n,难度分别为a1~an,可以在任何地方休息,每次休息难度将重置为a1开始.求总难度的数学期望. 解题思路: 跟这题很像,利用期望的可加性,我们分析每个位置的状态,不管怎么 ...

  8. Sentry 高级使用教程

    Sentry 高级使用教程 Sentry versions https://github.com/getsentry/sentry-docs https://github.com/getsentry/ ...

  9. Emoji of github & twitter

    Emoji cheat sheet http://www.emoji-cheat-sheet.com/ https://github.com/xgqfrms/emoji-cheat-sheet.com ...

  10. website i18n and L10n all in one

    website i18n and L10n all in one Localization & Internationalization 本地化 & 国际化 https://www.w ...