java中常见的单例模式详解

      很多求职者在面试过程中都被问到了单例模式,最常见的问题,比如,每种单例模式的区别是什么?哪些模式是线程安全的?你们项目里用的哪种单例模式?原来没有注意这个问题,回来赶紧打开项目查看了一下代码,才发现我们的项目用到了枚举。有的面试官还会让你手写一种单例模式,我建议大家就写自己项目中用到的那种。下面我就说一说我学到的单例模式。

一、单例模式的好处

     减少系统资源的消耗。因为这种工具类基本上贯穿程序始终,必然会频繁调用.如果每一次调用都要重新生成实例,那么在内存堆中就会分配相应的内存空间,所以使用单例模式会提高程序的运行速度,减少资源消耗。

二、单例模式的特点

     1、单例类只能有一个实例:确保某个类只有一个实例

     2、单例类必须自己创建自己的唯一实例:在需要创建单例模式的这个类里,自己new了一个自己的对象

     3、单例类必须给所有其他对象提供这一实例:因为单例类的构造是privite的,所以要给外部提供一个方法来访问这个实例

三、常见单例模式的使用方法和优缺点

     1、饿汉模式,可用。在初始化的时候就完成实例化了,避免了线程同步的问题,缺点就是无论这个类是否被使用,都会创建一个instance对象,不能延迟加载。如果从始至终都没有用过,造成内存浪费。在类加载的时候就完成了实例化,没有实现延迟加载。

public class Singleton {  
//实例化对象 private static Singleton instance = new Singleton();
//私有构造 private Singleton (){}
//对外提供一个公共的方法访问这个实例 public static Singleton getInstance() { return instance; } }

     2、饿汉变种,线程安全,可用。将类实例化的过程放在了静态代码块中,也是在类加载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。

 public class Singleton {  
      private Singleton instance = null;  
      static {  
      instance = new Singleton();  
      }  
      private Singleton (){}
      public static Singleton getInstance() {  
      return this.instance;  
      }  
 }  

     3、懒汉模式,线程不安全,不推荐用。多线程的时候不能保证实例是唯一的了。比如线程a要使用Singleton,第一次调用发现instance为null,开始创建实例还没完成创建,就在这个时候,CPU去执行线程b了,b发现instance也为null,还会去创建实例。b创建完之后,回来执行a。线程a不会再回去检查instance是否为null了,这样线程a和b各自拥有一个实例,单例失败。但是可以实现延迟加载。

public class Singleton {  
//没有final private static Singleton instance;
//私有构造 private Singleton (){}
//对外提供一个公共方法来访问这个实例 public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }

       4、懒汉模式,线程安全。解决饿汉模式带来的问题,就是在创建实例的时候添加一把锁,一个线程必须等待另外一个线程创建完成后才能调用这个方法,这就保证了单例的唯一性了。但是synchronized修饰的同步块要比一般的代码段慢上几倍的,如果多次调用getInstance()这个方法,会影响性能,可以实现延迟加载。

 public class Singleton {  
//没有final,因为下边要对instance重新赋值 private static Singleton instance;
//私有构造 private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }

     5、静态内部类,线程安全,推荐用。实现了延迟加载,减少内部开销,内部类SingletonHolder 只有在getInstance()方法第一次调用的时候才会被加载,内部类加载的时候实现了加载一下INSTANCE 。

public class Singleton {  
//内部类实例化INSTANCE  private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){}
//对外提供一个公共方法访问INSTANCE  public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }

     6、双重校验,线程安全,推荐用。既实现线程安全,又能够使性能不受到很大的影响,因为只有在第一次创建实例的时候才会走同步块。

 public class Singleton { 
//双重校验关键字volatile,被volatile修饰的值,将不会被本地线程缓存,保证所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确处理该变量,但是使用volatile运行效率并不高。不建议大量使用双重校验。 private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() {
//第一次校验,是否实例化了 if (singleton == null) {
//第二次校验,同步块,线程安全的创建实例 synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }    

四、单例模式适用的场合

     1、需要频繁的进行创建和销毁的对象

     2、创建对象时耗时过多或耗费资源过多,但又经常用到的对象

     3、工具类对象

     4、频繁访问数据库或文件的对象

  

  

  

原文地址:https://www.cnblogs.com/chentong/p/5604406.html