一、是什么
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
二、适用环境
单例模式可以保证全局对象的唯一性,比如系统启动读取配置文件就需要单例保证配置的一致性。故以下情况常考虑单例模式:
1)系统只需要一个实例对象,或者因为资源消耗太大只允许创建一个实例对象。
2)客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
三、实现方式
3.1 饿汉式单例
当类Singleton1被加载时,类的静态变量instance会被初始化,此时类的构造函数会被调用,单例类的唯一实例将被创建。
优点:无需考虑多线程问题;可以确保实例唯一性。调用速度快,反映时间短。
缺点:资源利用效率低,占用率高。
public class Singleton1 { private static Singleton1 instance = new Singleton1(); private Singleton1(){} public static Singleton1 getInstance(){ return instance; } }
3.2 懒汉式单例
当类Singleton2被加载时,不会创建类的实例,在需要的时候再加载实例,这种技术又叫延迟加载(Lazy Load)。
通过关键字synchronized解决线程安全问题。
优点:资源利用率高。
缺点:线程安全控制繁琐;系统性能会受影响。
public class Singleton2 { private static Singleton2 instance = null; //静态私有成员变量 //私有构造函数 private Singleton2(){ } //为避免多个线程同时调用getInstance()方法,使用关键字synchronized对方法加锁, //确保任意时刻只有一个线程可执行该方法 synchronized public static Singleton2 getInstacne(){ if (instance == null) { instance = new Singleton2(); } return instance; } }
上述代码虽然解决了线程安全的问题,但是每次调用getInstance()方法时,都要进行线程锁定判断,在多线程高并发环境下会导致系统性能降低,为解决此类问题问题,应将加锁范围缩小到创建实例时。故做如下改进:
public class Singleton2 { private static Singleton2 instance = null; //静态私有成员变量 //私有构造函数 private Singleton2(){ } public static Singleton2 getInstance(){ if (instance == null) { synchronized (Singleton2.class) { instance = new Singleton2(); } } return instance; } }
3.3 双重检查锁定
上述3.2懒汉式单例类仍然存在单例对象不唯一的情况,例如某一瞬间线程A和线程B同时调用getInstance()方法,再synchronized加锁机制下,A线程先执行加锁代码块创建实例,线程B处于等待状态。当A执行完毕时,线程B并不知道实例已被创建,将会继续创建一份新的实例,这样就产生了多个实例对象。
注意:使用双重检查锁定实现懒汉式单例类时,需要在静态成员变量instance前增加修饰符volatile,被volatile修饰的成员变量可以确保多个线程都能正确处理,但是需要JDK1.5以上(现在也不用更低的版本了吧...)。
优点:解决懒汉式单例类存在单例不唯一的问题。
缺点:volatile关键字会屏蔽Java虚拟机做的一些代码优化,有时可能会导致系统的运行效率降低。
故将继续改进如下:
public class Singleton3 { private volatile static Singleton3 instance = null; private Singleton3(){ } public static Singleton3 getInstance(){ //第一重判断 if (instance == null) { //锁定代码块 synchronized (Singleton3.class) { //第二重判断 if (instance == null) { instance = new Singleton3(); } } } return instance; } }
3.4 静态内部类实现单例模式
饿汉式单例类不能实现延迟加载,不管将来用不用单例类,始终都会占据内存;
懒汉式单例类线程安全控制繁琐,且性能受到影响;
在Java中,还可使用静态内部类实现单例模式,既可以实现延迟加载,又可以保证线程安全,不影响系统性能。
public class Singleton4 { private Singleton4(){} private final static class innerClass{ private static Singleton4 instance = new Singleton4(); } public static Singleton4 getInstance(){ return innerClass.instance; } /*//测试代码段 public static void main(String[] args){ Singleton4 s1,s2; s1 = Singleton4.getInstance(); s2 = Singleton4.getInstance(); System.err.println("s1 = s2? 答案是 " + (s1==s2)); //s1 = s2? 答案是 true }*/ }
备注:以上代码实现部分源码参照《Java设计模式》-(刘伟)一书。
总结如有错误、不足与改进之处,希望大家指正,不胜感激!
以上!