单例模式

定义:保证一个类只有一个实例,并且提供一个访问它的全局访问点。构造器私有化,提供静态方法getInstance()得到类的示例。
七种写法:
1.饿汉式

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

类加载时就实例化对象,典型的以空间换时间,需要调用对象时不需要判断是否实例化过,节省运行时间,但是若没用到该对象仍要实例化对象浪费内存。
2.懒汉式

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

第一次调用对象时才实例化对象,节约内存但是第一次调用对象需要实例化,反映稍慢。并且线程不安全,并发场景下会多次实例化。
3.懒汉式

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

在getInstance()方法上加synchronized锁,第一次调用对象时才实例化对象,节约内存,但是多线程情况下会造成线程阻塞,只有一个线程执行完同步方法,下一个线程才会获得对象锁执行同步方法,性能较差。
4.双重校验锁(DCL)

public class Singleton {

	/**
     * 注意此处使用的关键字 volatile,
     * 被volatile修饰的变量的值,将不会被本地线程缓存,
     * 所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。
     */
    private volatile static Singleton singleton;
    private Singleton() {
    }
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return singleton;
    }
}

这种写法在getInstance()中进行了两次判空,第一次是为了不必要的同步,第二次是在instance等于null的情况下才创建实例。
为什么要加volatile呢?instance = new Singleton()时,新建一个对象可以分为三个部分:位对象分配内存空间,初始化对象和将对象内存地址赋值给instance实例对象。JVM为了提高性能会进行代码重排序,将初始化对象排在将内存地址赋值给instance对象后。这样导致多个线程并发调用对象时,一些线程会调用到未初始化的对象。如果对象用volatile修饰,在多线程环境下会禁止重排序。
5.静态内部类

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

采用内部类,在内部类里创建对象实例。这样不使用内部类的话,JVM就不会实例化单例对象,实现懒汉式的延迟加载。当调用getInstance方法时会调用内部类进行加载。JVM进行类加载初始化时会保证同步性(所以线程安全!)。每个类都有一个唯一的初始化锁LC,线程需要获取类的初始化锁进行类的加载初始化。类初始化时static修饰的单例变量就会实例化(且只实例化一次!),这样保证了只实例化一次。
6.枚举
《Java与模式》中,作者这样写道,使用枚举来实现单实例控制会更加简洁,而且无偿地提供了序列化机制,并由JVM从根本上提供保障,绝对防止多次实例化,是更简洁、高效、安全的实现单例的方式。

public enum Singleton {
	 //定义一个枚举的元素,它就是 Singleton 的一个实例
     INSTANCE;  
     
     public void doSomeThing() {  
	     // do something...
     }  
 }

使用方法如下:

public static void main(String args[]) {
	Singleton singleton = Singleton.instance;
	singleton.doSomeThing();
}

枚举单例的优点就是简单,但是大部分应用开发很少用枚举,可读性并不是很高,不建议用。
7.使用容器

public class SingletonManager { 
  private static Map<String, Object> objMap = new HashMap<String,Object>();
  private Singleton() { 
  }
  public static void registerService(String key, Objectinstance) {
    if (!objMap.containsKey(key) ) {
      objMap.put(key, instance) ;//添加
    }
  }
  public static ObjectgetService(String key) {
    return objMap.get(key) ;//获取
  }
}

这种是用SingletonManager 将多种单例类统一管理,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值