单例模式-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 }
原文地址:https://www.cnblogs.com/feimaoyuzhubaobao/p/10105995.html