单例模式

定义:

  单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。即一个类只有一个对象实例。

特点:

       1、单例类只能有一个实例。   2、单例类必须自己自己创建自己的唯一实例。   3、单例类必须给所有其他对象提供这一实例。

单例模式的要点:

    1,私有的构造方法

    2,指向自己实例的私有静态引用

    3,以自己实例为返回值的静态的公有的方法

单例模式分类:

懒汉式(两种)、饿汉式(两)、静态内部类、枚举、双重校验锁等共七种实现方式。

实现:

第一种(懒汉,线程不安全):

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
      if (instance == null) {            //当多条线程到达此处时,可能会出现多条线程都通过if判断,继而创建对象,导致创建单例对象失败
          instance = new Singleton();  
      }  
      return instance;  
    }  
}  

第二种(懒汉,线程安全):

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  //通过加锁,使同时只有一条线程访问到该方法,线程安全,但是效率低下
      if (instance == null) {  
          instance = new Singleton();  
      }  
      return instance;  
    }  
}  

第三种(饿汉式)

public class Singleton {  
    private static Singleton instance = new Singleton();  //类加载就实例化,但是类什么时候加载,这个是不太确定的,大部分情况是调用getInstance()时加载的。
    private Singleton (){}  
    public static Singleton getInstance() {  
      return instance;  
    }  
}  

第四种(饿汉式,变种)

public class Singleton {  
    private static Singleton instance = null;  
    static {                  //根据java的类加载机制,在类加载的时候,静态代码块就运行并进行了实例化,实际上跟第三种是差不多的
      instance = new Singleton();  
    }  
    private Singleton (){}  
    public static Singleton getInstance() {  
      return instance;  
    }  
}  

第五种(静态内部类)

public class Singleton {  
    private static class SingletonHolder {  
      private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
      return SingletonHolder.INSTANCE;  
    }  
}  

当类Singleton被加载时,并不会立刻进行实例化(SingletonHolder未被使用就不会主动加载,就不会实例化,实现lazy loading),可以实现当实例化需要消耗巨大资源时延迟加载,也可以避免主动实例化(目前这个场景还没有想到)

第六种(枚举)

public enum Singleton {  //这个模式博主暂时没有搞明白。如果有大佬看到的话可以帮忙指出
    INSTANCE;  
    public void whateverMethod() {  
    }  
}  

特点:类似于饿汉式,代码简单,自有序列化。

2017/12/21

枚举本身实际上就是一个class

实际上枚举类型中的每一个枚举(INSTANCE)都是枚举类的一个实例对象。

枚举的构造方法时私有的。所以可以在一定程度上实现单例

第七种(双重校验锁)

public class Singleton {  
    private volatile static Singleton singleton;  //看过一篇博文,说在java1.2之后就可以避免已经实例化后变量无法指向实例的地址值(实例化后会保证变量一定能拿到其指向实例的内存地址)
    private Singleton (){}               //也就是说解决了安全性问题。
    public static Singleton getSingleton() {    //但是也有博文说,在java1.5之后才能正常使用(具体的还没有找到博文比较尴尬)
    if (singleton == null) {  
        synchronized (Singleton.class) {  
          if (singleton == null) {  
              singleton = new Singleton();  
          }  
        }  
    }  
    return singleton;  
    }  
}  

总结:

有两个问题需要注意:

1.如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。

2.如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。

对第一个问题修复的办法是:

private static Class getClass(String classname)      
                                         throws ClassNotFoundException {     //这段代码,其实我看不懂。。。。
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();     
      
      if(classLoader == null)     
         classLoader = Singleton.class.getClassLoader();     
      
      return (classLoader.loadClass(classname));     
   }     
}  

 对第二个问题的修复方法:

public class Singleton implements java.io.Serializable {     
   public static Singleton INSTANCE = new Singleton();     
      
   protected Singleton() {     
        
   }     
   private Object readResolve() {   //区别也就是说返回值类型变了,这里我也不太懂。有点懵逼    
            return INSTANCE;     
      }    
}   

 一般想要实现懒加载可以选用第五种方法,双重校验锁也是很好的选择(线程安全)

如果看到我文章的大佬觉得哪里有问题,欢迎您指正。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏码洞

Java与CPU缓存的亲密接触之「伪共享」

这个程序的逻辑是4个线程共享同一个数组读写不同下标的变量。每个线程循环1亿次读写,也就是+1操作。然后统计4个线程同时跑完总共花的时间。

844
来自专栏技术专栏

慕课网高并发实战(二)-并发基础

左图为最简单的高速缓存的配置,数据的读取和存储都经过高速缓存,CPU核心与高速缓存有一条特殊的快速通道;主存与高速缓存都连在系统总线上(BUS)这条总线还用于其...

963
来自专栏java一日一条

java高并发锁的3种实现

提到锁,大家可能都会想到synchronized关键字,使用它的确可以解决一切并发问题,但是对于系统吞吐要求更高的,在这里提供了几个小技巧,帮助大家减小锁粒度,...

843
来自专栏猿人谷

多核环境下cache line的测试

前阵子接触到一道关于数组内部链表(多用于内存池技术)的数据结构的题, 这种数据结构能够比普通链表在cache中更容易命中, 理由很简单, 就是因为其在地址上是连...

1709
来自专栏用户2442861的专栏

操作系统内存管理——分区、页式、段式管理

内存管理主要包括虚地址、地址变换、内存分配和回收、内存扩充、内存共享和保护等功能。

411
来自专栏https://www.cnblogs.com/L

【Spark篇】---Spark调优之代码调优,数据本地化调优,内存调优,SparkShuffle调优,Executor的堆外内存调优

Spark中调优大致分为以下几种 ,代码调优,数据本地化,内存调优,SparkShuffle调优,调节Executor的堆外内存。

923
来自专栏灯塔大数据

干货 | 高级Java面试通关知识点整理!

682
来自专栏岑玉海

Hive Tuning(四) 从查询计划看hive.auto.convert.join的好处

今天我们来讲一下如何看懂Hive的查询计划。 hive的执行计划包括三部分 – Abstract syntax tree – 可以直接忽略  – Sta...

2747
来自专栏linjinhe的专栏

Linux进程内存管理(二)

进程启动后,在 jemalloc 载入的时候会调用 jemalloc_constructor 执行一些初始化操作。这里利用了编译器的一些特殊支持,让函数在库加载...

1904
来自专栏性能与架构

MySql缓存中的关键项

MySql的设计中大量使用了缓存,下面这些缓存配置项是应该熟知的 key_buffer_size key_buffer_size是设置MyISAM表索引的缓冲区...

3405

扫码关注云+社区