概述
单例模式(Singleton Pattern)属于创建型模式,它提供了一种创建单一对象的方式。
单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常,我们在new一个对象时,不能防止它被实例化多次,怎样保证它只被实例化一次呢,一个最好的办法就是让这个类自身去创建并保存它的唯一实例,再提供一个访问该实例的公共方法,那么就能保证它被实例化一次了。
说完单例模式的分类和概念,我们再说说它的具体应用。在开发和设计过程中什么时候会用到单例模式呢,什么时候我们的类只需要实例化一次呢,有些时候我们只是想节约系统资源,但有些时候我们因为功能需求只能实例化一个对象,比如:
- 大家基本上天天都在接触的,windows的任务管理器;
- web中的计数器,一般使用单例做缓存;
- web中读取配置对象
- 数据库连接池
- 序列号生成
- ...
关于单例模式的应用场景,具体可以参考http://www.cnblogs.com/BrainDeveloper/p/3192417.html
本文着重写单例模式的四种实现,包括懒汉式、饿汉式、静态内部类,登记式单例模式。我知道网上已经写了很多这方面的总结了,但我也想尽可能按照自己的理解来总结一下。
1、懒汉式单例模式
我们先直接来看这段代码:
package designpatterns.singleton;
/**
* 懒汉式单例模式
* @Description: 在被引用时才会实例化自己
* @author YanTu
* @date 2017年4月25日 上午10:07:52
*/
public class Singleton1 {
private static Singleton1 instance;
//构造方法私有化,防止外界利用new创建此实例的可能
private Singleton1() {
}
//此方法是获得本类实例的唯一全局访问点
public static Singleton1 getInstance(){
//若实例不存在,则new一个新实例,否则返回已有的实例
if(instance == null){
instance = new Singleton1();
}
return instance;
}
}
客户端测试代码:
package designpatterns.singleton;
/**
* 单例模式测试
* @Description: 测试是否为单例
* @author YanTu
* @date 2017年4月25日 上午9:59:17
*/
public class SingletonTest {
public static void main(String[] args) {
Singleton1 s1 = Singleton1.getInstance();
Singleton1 s2 = Singleton1.getInstance();
if(s1 == s2){
System.out.println("两个对象是相同的实例");
}
}
}
我们可以看出单例模式除了封装它的唯一实例以外,还可以严格的控制访问,所以单例模式的最佳理解就是对唯一实例的受控访问。
以上代码我们可以看出
懒汉式单例模式仅仅在客户端引用getInstance()方法时才创建实例,因此才被称之为懒汉式单例。但是,这种类型的单例模式,在多线程环境下会出现安全问题。比如多个线程同时访问Singleton1类,调用getInstance()方法,会有可能创建多个实例的。那么这时候我们就会想到在getInstance()方法上加一个同步锁,代码如下:
public static synchronized Singleton1 getInstance(){
//若实例不存在,则new一个新实例,否则返回已有的实例
if(instance == null){
instance = new Singleton1();
}
return instance;
}
这样就保证了在多线程环境下同时访问不会造成多个实例的生成。但是新的问题又来了,不管这个实例是否已存在,每个线程进来都要排队,这样做很大的影响性能,对于高并发的系统来说是致命的,所以还得继续优化:
package designpatterns.singleton;
/**
* 懒汉式单例模式
* @Description: 在被引用时才会实例化自己
* @author YanTu
* @date 2017年4月25日 上午10:07:52
*/
public class Singleton1 {
private static Singleton1 instance;
//构造方法私有化,防止外界利用new创建此实例的可能
private Singleton1() {
}
//此方法是获得本类实例的唯一全局访问点
public static synchronized Singleton1 getInstance(){
if(instance == null){
//保证线程同步
synchronized (Singleton1.class) {
//若实例不存在,则new一个新实例,否则返回已有的实例
if(instance == null){
instance = new Singleton1();
}
}
}
return instance;
}
}
现在这样,我们就不用让线程每次访问都加锁,而是在实例未被创建的时候再加锁处理,同时也能保证多线程的安全,这种做法叫做
双重锁定。
我们需要注意的是synchronized中不能是instance,也就是说不能用对象锁,只能用类锁,因为这里instance实例还未被创建,所以不能对它加锁。
可能还有人会问为什么要对instance做两次判空,这个时候我们思考一下就知道了,如果instance为null并且同时有两个线程调用getInstance时,它们将通过第一次判断,然后由于同步锁机制,仅有一个进程进入,另一个则排队等候,而此时如果没有第二次判空操作,第一个进程创建了实例,第二个进程还是可以继续创建新的实例,这样就达不到单例的目的了。
其实除了懒汉式单例模式,另外几种单例模式的实现都不会有线程安全的问题,接下来我们先看看饿汉式的实现。
2、饿汉式单例模式
我们先来看看代码:
package designpatterns.singleton;
/**
* 饿汉式单例模式
* @Description: 类加载时就会实例化自己
* @author YanTu
* @date 2017年4月25日 下午3:30:59
*/
public class Singleton2 {
private static Singleton2 instance = new Singleton2();
private Singleton2(){
}
public static Singleton2 getInstance(){
return instance;
}
}
饿汉式单例模式,顾名思义,加载的时候就开始实例化了,事先就准备好了,什么时候要就什么时候取。因为提前实例化,也避免了懒汉式单例中所出现的线程安全问题。
不过这种实现的问题是,不管我们是否引用,内存中都会有这么一个实例,这样势必会降低内存的使用率,有没有什么方法可以避免呢,有,延迟加载,也就是下面这种实现方式。
3、静态内部类形式的单例模式
package designpatterns.singleton;
/**
* 静态内部类形式的单例模式
* @Description: 使用SingletonHolder静态内部类持有单例对象,则可在引用时再加载实例,以达到懒加载的目的
* @author YanTu
* @date 2017年4月25日 下午4:11:49
*/
public class Singleton3 {
private Singleton3(){
}
private static class SingletonHolder{
private final static Singleton3 INSTANCE = new Singleton3();
}
public static Singleton3 getInstance(){
return SingletonHolder.INSTANCE;
}
}
静态内部类形式的单例模式通过在内部类中定义静态属性,并在引用getInstance()方法时再进行加载并获取,以此达到了懒加载的目的,因此在内存使用率、线程安全上都有较大的优势。
以上3种实现,都存在反射和反序列化漏洞,也就是说针对上面几种单例的实现,通过反射方式仍然可以创建多个单例,而让模式失效。不过已经有同仁在研究这个漏洞了,比如:http://blog.csdn.net/hardwin/article/details/51477359
4、登记式单例模式
登记式单例实际上是对一组单例进行维护,通过map保存,在调用时先判断单例是否已经创建,是则直接返回,否则创建一个并登记到map中,并返回。这种方式有点像爬虫队列的遍历,访问过则放入已访问队列,只不过这里是map而已。spring容器对于单例Bean的管理通常会用到这种模式,这里就暂时不做补充了。看到大家关于单例模式的分享里面还有枚举形式的实现,本人暂未做研究,待以后详细研究后再做补充。
最后
以上就是复杂鸡翅为你收集整理的单例模式的几种实现的全部内容,希望文章能够帮你解决单例模式的几种实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复