6.22Java多线程单例设计模式

6.22Java多线程单例设计模式

设计一个单例模式

类与类之间的关系

目标:对外只有一个对象

介绍double-checking单例模式

使用volatile进行锁定资源

饿汉式:直接实例化了对象

懒汉式:没有直接实例化对象

DCL单例设计模式实例demo

图示:

 

package thread.rearrangement;

/**
* DCL单例模式:
* 1、在多线程环境下,对外存在一个对象--->外部不能new对象--->构造器私有化,避免外部new构造器(懒汉式加入并发控制)
* 2、内部提供私有的静态属性--->存储对象的地址
* 3、对外部提供公共的静态方法--->获取属性(该属性存了对象的地址)
* @since JDK 1.8
* @date 2021/6/22
* @author Lucifer
*/
public class DoubleCheckedLocking {

   /*提供私有的静态属性*/
   private static volatile DoubleCheckedLocking instance;
   //如果没有volatile其他线程可能访问到一个没有初始化的对象

   /*构造器私有化*/
   private DoubleCheckedLocking(){

  }

   /*提供公共的静态方法访问私有属性*/
   public static DoubleCheckedLocking getInstance(){

       /*如果已经存在对象,直接返回--->doublechecking避免不必要的同步(已经存在对象)*/
       if (null!=instance){
           /*直接返回结果*/
           return instance;
      }

       /*加入同步块锁住类--->class对象*/
       synchronized (DoubleCheckedLocking.class){
           /*当没有对象的时候返回对象*/
           if (null==instance){

               /*创建一个对象*/
               instance = new DoubleCheckedLocking();

               /*
               在这里考虑指令重排
               在实例化一个对象的时候步骤:
               1、开辟空间
               2、初始化对象信息(对、块、栈)
               3、返回对象地址给引用
               如果构造器很慢,就有可能下面的内容先执行。
               举例:
               A还在初始化对象
               B已经拿到了引用
               就会形成空对象!!!
               如何避免?
               在属性前加入volatile保证可见性
                */
          }
      }

       /*返回类对象*/
       return instance;

  }

   public static void main(String[] args) {
       Thread t = new Thread(()-> {
           System.out.println(DoubleCheckedLocking.getInstance());
      });
       t.start();

       if (t.equals(DoubleCheckedLocking.getInstance())){
           System.out.println(DoubleCheckedLocking.getInstance());
      }else {
           System.out.println("Error!");
      }

       /*直接输出DoubleCheckedLocking.getInstance就不会出现指令重排*/
       System.out.println(DoubleCheckedLocking.getInstance());

       /*
       这里也发生了指令重排的情况
       因为Thread是实例化线程,DoubleCheckedLocking.getInstance是直接获取地址
       所以先执行了下面的if判断
       所以执行的结果是先打印处了Error后打印出了getInstance的地址
        */

  }
}
/*
1、如果两个线程A、B进入线程
2、A进入创建对象但是耗时时间长
3、B进入时候A对象没有写回主存,导致B也创建了一个对象
这样就形不成单例设计模式了,所以要加入同步块
1、锁住类的.class对象(类的模子)
2、多个线程进入,就不会造成多线程创建多个对象
如果已经存在对象则不需要等待,所以需要doublechecking再次监测
*/

 

It's a lonely road!!!
原文地址:https://www.cnblogs.com/JunkingBoy/p/14921557.html