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

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>()这个类里面没有空参构造器
 
 
    }
 }

枚举为有参构造 两个参数

image-20210224090220613

 

 

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

image-20210224094835049

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

 // 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());
 
    }
 }
 

 

image-20210224101621763

 

image-20210224102330931

image-20210224103027295

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

缺点:

1.循环会耗时

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

3.存在ABA问题

 

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

image-20210224103733040

 

 
 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 一定会创建新的对象分配新的内存空间;

image-20210224112523025

 

 

21.各种锁的理解

1.公平锁,非公平锁

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

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

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

 

2.可重入锁

可重入锁(递归锁)

image-20210224115413284

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

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

image-20210224121541857

 

自定义锁来测试

 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.死锁

 

image-20210224151714055

死锁测试,怎么排除死锁

死锁:

 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定位进程号

image-20210224153259029

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

image-20210224153744719

image-20210224153712390

 

 

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

1.日志

2.查看堆栈信息

posted @ 2021-02-24 19:43  豆豆tj  阅读(156)  评论(0)    收藏  举报