【简介】
单例模式(Singleton)保证一个类仅有一个实例,并提供一个访问它的全局访问点。
实现单例模式的一个最好的方法就是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以创建,并且它可以提供一个访问该实例的方法。
【特点】
单例模式具有一下特点:
- 单例类只有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给所有其他对象提供这一实例
【分类】
主要的就是懒汉单例,饿汉单例
【懒汉单例】
package Mode; /** * Java设计模式之单例模式 * @author SJF0115 * */ public class Singleton { private static Singleton instance; // 构造方法让其private,这就堵死了外界利用new创建此类实例的可能 private Singleton() { } // 此方法是获得本实例的唯一全局访问点 public static Singleton GetInstance(){ // 若实例不存在,则new一个新实例,否则返回已有的实例 if(instance == null){ instance = new Singleton(); } return instance; } /** * @param args */ public static void main(String[] args) { Singleton s1 = Singleton.GetInstance(); Singleton s2 = Singleton.GetInstance(); if(s1 == s2){ System.out.println("两个对象是相同的实例"); } else{ System.out.println("两个对象不是相同的实例"); } } }
这种写法lazy loading很明显,但是致命的是在多线程不能正常工作(多线程不安全)。
在类被加载的时候,唯一实例已经被创建。这个设计模式在Java中容易实现,在别的语言中难以实现。
【饿汉单例】
package Mode; /** * Java设计模式之单例模式 * @author SJF0115 * */ public class Singleton { // 类装载时就实例化 private static Singleton instance = new Singleton(); // 构造方法让其private,这就堵死了外界利用new创建此类实例的可能 private Singleton() { } // 此方法是获得本实例的唯一全局访问点 public static Singleton GetInstance(){ return instance; } /** * @param args */ public static void main(String[] args) { Singleton s1 = Singleton.GetInstance(); Singleton s2 = Singleton.GetInstance(); if(s1 == s2){ System.out.println("两个对象是相同的实例"); } else{ System.out.println("两个对象不是相同的实例"); } } }
这种方式基于classloder机制避免了多线程的同步问题,因此饿汉单例是线程安全的。
不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法,
但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。
【懒汉单例(线程安全)】
package Mode; /** * Java设计模式之单例模式 * @author SJF0115 * */ public class Singleton { private static Singleton instance; // 构造方法让其private,这就堵死了外界利用new创建此类实例的可能 private Singleton() { } // 此方法是获得本实例的唯一全局访问点 public static Singleton GetInstance(){ // 若实例不存在,则new一个新实例,否则返回已有的实例 if(instance == null){ synchronized(Singleton.class){ if(instance == null){ instance = new Singleton(); } } } return instance; } /** * @param args */ public static void main(String[] args) { Singleton s1 = Singleton.GetInstance(); Singleton s2 = Singleton.GetInstance(); if(s1 == s2){ System.out.println("两个对象是相同的实例"); } else{ System.out.println("两个对象不是相同的实例"); } } }
有人可能会疑问,在GetInstnce方法中为什么会有两次判断if(instance == null)。
回答:
对于instance存在的情况,就直接返回。不需要考虑线程问题。当instance为null时,就需要我们考虑多线程带来的问题。
假设当instance为null,并且同时有两个线程调用GetInstance()方法,他们都可以通过第一重instance == null的判断。
然后由于Synchronized机制,这两个线程只有一个进入,另一个在外排队等候,必须等先进入的线程结束之后,才能进入。
而此时如果没有第二重instance == null 判断,第一个线程进入后创建一个实例,第一个线程进入出来后,第二个线程终于等待结束,可以进入,
继续又创建了一个实例,这没有达到单例的目的。