单例模式-Singleton Pattern
单例模式-Singleton Pattern
何为单例模式,举个大家都熟知的例子—— Windows任务管理器,在Windows的“任务栏”的右 键弹出菜单上多次点击“启动任务管理器”,是不能同时打开多个任务管理器窗口的。我们用代码实现下:
假设任务管理器的类名为TaskManager,在 TaskManager类中包含了大量的成员方法,例如构造函数TaskManager(),显示进程的方法
1 package com.feimao.singleton.test; 2 3 public class TaskManager { 4 public void displayProcess() { 5 } 6 7 public void displayServices() { 8 } 9 10 private TaskManager() {//禁止类的外部直接使用new来创建对象,所以将构造函数TaskManager()可见性改为private 11 } 12 13 public static TaskManager tm = null;//TaskManager中定义一个静态的TaskManager类型的私有成员变量,目的是为了让外界可以访问这个唯一实例 14 public static TaskManager getInstance() {//是增加一个公有的静态方法,在类外可以直接通过类名来访问,而无须创建TaskManager对象 15 if (tm == null) { 16 tm = new TaskManager(); 17 } 18 return tm; 19 } 20 }
在类外我们无法直接创建新的TaskManager对象,但可以通过代码TaskManager.getInstance()来 访问实例对象,第一次调用getInstance()方法时将创建唯一实例,再次调用时将返回第一次创 建的实例,从而确保实例对象的唯一性。
单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
因进程需要,有时我们只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计。
单例模式的特点:
1.单例模式只能有一个实例。
2.单例类必须创建自己的唯一实例。
3.单例类必须向其他对象提供这一实例
单例模式VS静态类:
在知道了什么是单例模式后,我想你一定会想到静态类,“既然只使用一个对象,为何不干脆使用静态类?”,这里我会将单例模式和静态类进行一个比较。
1.单例可以继承和被继承,方法可以被override,而静态方法不可以。
2.静态方法中产生的对象会在执行后被释放,进而被GC清理,不会一直存在于内存中。
3.静态类会在第一次运行时初始化,单例模式可以有其他的选择,即可以延迟加载。
4.基于2, 3条,由于单例对象往往存在于DAO层(例如sessionFactory),如果反复的初始化和释放,则会占用很多资源,而使用单例模式将其常驻于内存可以更加节约资源。
5.静态方法有更高的访问效率。
几个关于静态类的误解:
误解一:静态方法常驻内存而实例方法不是。
实际上,特殊编写的实例方法可以常驻内存,而静态方法需要不断初始化和释放。
误解二:静态方法在堆(heap)上,实例方法在栈(stack)上。
实际上,都是加载到特殊的不可写的代码内存区域中。
静态类和单例模式情景的选择:
情景一:不需要维持任何状态,仅仅用于全局访问,此时更适合使用静态类。
情景二:需要维持一些特定的状态,此时更适合使用单例模式。
单例模式的实现:
1.懒汉模式(线程不安全)
1 package com.feimao.a3.test; 2 3 public class SingletonDemo { 4 private static SingletonDemo instance; 5 6 private SingletonDemo() { 7 8 } 9 10 public static SingletonDemo getInstance() { 11 if (instance == null) { 12 instance = new SingletonDemo(); 13 } 14 return instance; 15 } 16 }
通过提供一个静态的对象instance,利用private权限的构造方法和getInstance()方法来给予访问者一个单例。
缺点是,没有考虑到线程安全,可能存在多个访问者同时访问,并同时构造了多个对象的问题。之所以叫做懒汉模式,主要是因为此种方法可以非常明显的lazy loading。
针对懒汉模式线程不安全的问题,我们自然想到了,在getInstance()方法前加锁,于是就有了第二种实现。
2.线程安全的懒汉模式(线程安全)
1 package com.feimao.a3.test; 2 3 public class SingletonDemo { 4 private static SingletonDemo instance; 5 private SingletonDemo(){ 6 7 } 8 public static synchronized SingletonDemo getInstance(){ 9 if(instance == null){ 10 instance = new SingletonDemo(); 11 } 12 return instance; 13 } 14 }
3.饿汉模式(线程安全)
1 package com.feimao.a3.test; 2 3 public class SingletonDemo { 4 private static SingletonDemo instance = new SingletonDemo(); 5 private SingletonDemo(){ 6 7 } 8 public static SingletonDemo getInstance(){ 9 return instance; 10 } 11 }
直接在运行这个类的时候进行一次loading,之后直接访问。显然,这种方法没有起到lazy loading的效果,考虑到前面提到的和静态类的对比,这种方法只比静态类多了一个内存常驻而已。
4.静态类内部加载(线程安全)
1 package com.feimao.a3.test; 2 3 public class SingletonDemo { 4 private static class SingletonHolder { 5 private static SingletonDemo instance = new SingletonDemo(); 6 } 7 8 private SingletonDemo() { 9 System.out.println("Singleton has loaded"); 10 } 11 12 public static SingletonDemo getInstance() { 13 return SingletonHolder.instance; 14 } 15 }