在学习单例模式之前,首先我们必须明白为何需要单例模式。即单例模式存在的意义。考虑到
这样一种场景:在整个应用中某个类只有一个独一无二的实例,也就是"单例",例如笔者在以前完
成的一个聊天程序的聊天窗口。有这样一种需求,即是当聊天窗口已经打开的时候不在实例化它。
基于上述描述,单例模式的存在是有其实际意义的!那么,何为单例模式呢?
单例模式:
单例模式指的是一个类仅有一个实例对象,并且提供对这个对象的一个全局访问点。
单例模式类图结构:
具体来说单例模式是如何实现的呢?下面是代码:
package com.kiritor; /** * @author Kiritor * 懒汉式单例模式*/ public class Singleton { private static final Singleton singleton = null; //私有构造方法 private Singleton() { } public static Singleton getInstance() { if(singleton==null) { singleton = new Singleton(); } return singleton; } }由上述代码可以看出Singleton类通过将构造方法限定为私有的(private),避免了类在外部
被实例化。Singleton的唯一实例只能通过getInstance方法得到。
Tips:实际上,通过反射机制是可以得到该实例对象的,不过这里姑且不论!而且上述实’
现在多线程的情况下也是不成立的。
观察上述代码可知,Singleton类中拥有一个Singleton类型的静态常量,由该成员的初始化
方式的不同,单例模式大体上可以分为两种。
1、懒汉式单例模式:
把对象的创建放在方法中去,延迟了创建(懒得)。也就是上述代码的实现。
2、饿汉式单例模式:
在声明对象的时候初始化(急不可耐),实现代码如下。
package com.kiritor; /** * @author Kiritor * 饿汉式单例模式*/ public class Singleton { private static Singleton singleton = new Singleton(); //私有构造方法 private Singleton() { } public static Singleton getInstance() { return singleton; } }那么,他们两者具体又有什么不同呢?
懒汉式模式,只有外部对象第一次请求实例的时候才会去创建实例,运行时获得对象的速度较慢
但是,加载类的时候速度较快,在其整个的周期中只有一部分时间占用资源。饿汉式模式正好与其
相反。
前面提到,上述单例模式并不是线程安全的,没有考虑到多线程的情况,解决方法如下:
1、 (使用锁机制:就是线程对临界区的访问权限)
package com.kiritor; /** * @author Kiritor * 懒汉式单例模式*/ public class Singleton { private static Singleton singleton = null; private static Object object = new Object(); //私有构造方法 private Singleton() { } public static Singleton getInstance() { /**这里事先并不知道singeton是否被创建,因此不对方法进行锁*/ synchronized(object) { if(singleton== null) { singleton = new Singleton(); } } return singleton; } }
上述代码还可以进一步优化: 2、使用双重锁
package com.kiritor; /** * @author Kiritor * 饿汉式单例模式*/ public class Singleton { private static Singleton singleton = null; private static Object object = new Object(); //私有构造方法 private Singleton() { } public static Singleton getInstance() { if(singleton== null) { synchronized(object) { /*这里在做一次的判断是: * 当两个线程同时运行到singleton=null时 * 只能有一个A线程运行,另一个B不能运行 * 但是当不做这层判断的时候,A执行完成后(有了一个实例a), * B开始执行,由于没有singeton==null的判断B结束之后有了一个实例b * 这样就不能保证单例了*/ if(singleton== null) { singleton = new Singleton(); } } } return singleton; } }
此方法保证了对象在生命周期内只能被锁定一次,因而不会影响性能!推荐