java 并发编程学习笔记(四)之 安全发布对象(单例模式实现几种实现方式)

举报
小米粒-biubiubiu 发表于 2020/12/03 01:28:12 2020/12/03
8.5k+ 0 0
【摘要】                                           安全发布对象 错误发布对象: 发布对象:使一个对象能被当前范围之外的代码所使用 ...

                                          安全发布对象

错误发布对象:

发布对象:使一个对象能被当前范围之外的代码所使用

对象溢出:一种错误的发布,当一个对象还没有构造完成时,就使它被其他线程所见

如何 安全 发布对象呢 ?一般采用 以下四种方式

(1)首先来看一个 非安全发布对象代码:


      @Slf4j
      public class UnSafePublish {
      private String[] states = {"a", "b", "c"};
      /**
       * 无法确保 其他的线程会通过调用此方法,来修改私有的成员变量
       *
       * @return
       */
      public String[] getStates() {
      return states;
       }
      public static void main(String[] args) {
       UnSafePublish unSafePublish = new UnSafePublish();
       log.info("{}", Arrays.toString(unSafePublish.getStates()));
       unSafePublish.getStates()[0] = "d";
       log.info("{}", Arrays.toString(unSafePublish.getStates()));
       }
      }
  
 

(2)非安全的 对象溢出


      public class Escape {
      private  int thisCanBeEscape =0 ;
      /**一种错误的发布,当一个对象还没有构造完成时,就使它被其他线程所见*/
      public Escape() {
      new InnerClass();
       }
      private class InnerClass{
      public InnerClass(){
      log.warn("{}",Escape.this.thisCanBeEscape);
       }
       }
      public static void main(String[] args){
      new Escape();
       }
      }
  
 

 (3)单例模式的 几种方式比较

  • 懒汉模式

      /**
       * 懒汉模式
       * 单例的实例在第一次使用的时候进行创建
       * <p>
       * 在单线程的环境下,没有问题,但是在多线程的环境下就会出问题。
       */
      @NotThreadSafe
      @Slf4j
      public class SingletonExample1 {
      //私有构造函数
      private SingletonExample1() {
       }
      //单例对象
      private static SingletonExample1 instance = null;
      //静态的工厂方法
      //懒汉模式本身时线程不安全的,加上了synchronized关键字之后就可以实现安全
      //但是降低了性能,因此并不推荐这种写法
      public static synchronized SingletonExample1 getInstance() {
      if (instance == null) {
       instance = new SingletonExample1();
       }
      return instance;
       }
      }
  
 
  • 饿汉模式 

      /**
       * 饿汉模式
       */
      public class SingletonExample2 {
      private SingletonExample2() {
       }
      private static SingletonExample2 singletonExample2 = new SingletonExample2();
      public static SingletonExample2 getInstance() {
      return singletonExample2;
       }
      }
  
 
  •  懒汉模式+ 双重检测同步锁

      /**
       * 懒汉模式 -》》双重同步锁单例模式
       */
      @NotThreadSafe
      @Slf4j
      public class SingletonExample3 {
      //私有构造函数
      private SingletonExample3() {
       }
      /**
       * 1.分配对象的内存空间
       * 2.初始化对象
       * 3.设置instance 指向刚分配的内存
       * 在单线程的情况下没有问题,但是在多线程的情况下
       *
       * jvm 和cpu 优化,发生指令重排
       *
       * 1.分配对象的内存空间
       * 2.设置instance 指向刚分配的内存
       * 3.初始化对象
       *
       * 因此我们需要限制SingletonExample3类的创建的时候的指令重排,添加volatile关键字
       */
      //单例对象
      //volatile 加上 双重检测机制,禁止指令重排
      private volatile static SingletonExample3 instance = null;
      //静态的工厂方法
      public static  SingletonExample3 getInstance() {
      if (instance == null) { //双重检测机制
      synchronized (SingletonExample3.class) { //同步锁
      if (instance == null) {
       instance = new SingletonExample3();
       }
       }
       }
      return instance;
       }
      }
  
 
  •  饿汉模式 (静态块的方式)

      /**
       * static 静态块的饿汉模式
       */
      public class SingletonExample6 {
      private SingletonExample6(){}
      private static SingletonExample6 instance =null;
      static  {
       instance = new SingletonExample6();
       }
      private static SingletonExample6 getInstance(){
      return instance;
       }
      public static void main(String[] args){
       System.out.println(getInstance());
       System.out.println(getInstance());
       }
      }
  
 
  • 枚举模式(最推荐) 

      /**
       * 枚举模式,最安全
       * 利用枚举的特性,一个枚举的构造方法只会被调用一次实现单例模式,推荐使用这种方式
       * <p>
       * 优点:1、相比懒汉模式,更能保证线程安全性
       * 2、相比饿汉模式,也只会在第一次使用的时候进行创建实例,不会造成资源浪费
       */
      @ThreadSafe
      @Recommend
      public class SingletonExample7 {
      private SingletonExample7() {
       }
      public static SingletonExample7 getInstance() {
      return Singleton.INSTANCE.getInstance();
       }
      private enum Singleton {
       INSTANCE; //只有一个枚举变量,保证构造方法只调用一次
      private SingletonExample7 singletonExample7;
      //jvm保证这个方法绝对只调用一次
       Singleton() {
       System.out.println("我只会被调用一次");
       singletonExample7 = new SingletonExample7();
       }
      public SingletonExample7 getInstance() {
      return singletonExample7;
       }
       }
      public static void main(String[] args) {
       System.out.println(SingletonExample7.getInstance());
       System.out.println(SingletonExample7.getInstance());
       System.out.println(SingletonExample7.getInstance());
       }
      }
  
 

文章来源: blog.csdn.net,作者:血煞风雨城2018,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/qq_31905135/article/details/84227362

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。