设计模式–单例模式
什么是单例模式
单例模式属于工厂模式的特例,只是它不需要输入参数并且始终返回同一对象的引用。
单例模式能够保证某一类型对象在系统中的唯一性,即某类在系统中只有一个实例。在《设计模式:可复用面向对象软件的基础》一书中对单例模式的适用性有如下描述:
- 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时
- 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
常见的几种单例模式
本文介绍常见的几种单例模式:懒汉式,饿汉式以及注册式
饿汉式单例
饿汉式单例在程序启动或单件模式类被加载的时候,单件模式实例就已经被创建,代码如下:
public class Single{
private static Single single=new Single();
private Single(){
}
public static SinglegetInstance(){
return single;
}
}
饿汉式的特点是首次加载时已经创建实例,如果不使用,存在浪费内存资源的可能性,但运行时获取对象的速度快,并且线程安全。
懒汉式单例
懒汉式单例:当程序第一次访问单例模式实例时才进行创建,代码如下:
public class Single {
private static Single single;
private Single(){}
public static Single getInstance(){
if(single==null){
single=new Single();
}
return single;
}
}
懒汉式的特点是懒汉模式的特点是加载类时比较快,但是在运行时获取对象的速度比较慢,线程不安全。若要保证懒汉式线程安全,需要在创建实例对象时添加synchronized关键字,代码如下:
public class Single {
private static Single single;
private Single(){}
public synchronized static Single getInstance(){
if(single==null){
single=new Single();
}
return single;
}
}
但是在创建实例对象时添加synchronized关键字,加锁和性能总是互斥的,保证了线程的安全,由于每次都要进入同步方法,牺牲了一定的性能
双检索单例
双检索单例是懒汉式单例基础上了改进,保证安全性的同时,也保证了性能。Spring底层创建单例对象就是基于双检索创建单例对象的,双检索单例代码如下:
public class Single {
//用volatile修饰
private volatile static Single single =null;
private Single(){}
public static Single getInstance(){
//第一次检索,判断对象是否存在,存在则不进入同步方法
if(single==null){
synchronized (Single .class){
if(single==null){
single=new Single();
}
}
}
return single;
}
}
首先需注意该方法使用的是volatile关键字,若不采用volatile关键字,在某些情况下是线程不安全(网上说的是重排序,内存不可见等,暂时还未了解);其次是在创建实例方法时添加了一次检索判断,这样就可以避免每次创建实例时都进入同步方法;而后在同步方法中进行第一次检索判断,这样的方式便称之为“双检索”
注册式单例-枚举法
以上几种方式的单例,都存在反射、序列化破坏单例的情况,而在jdk1.5中添加了枚举类,通过枚举类,我们可以很好的解决以上问题,有很好的避免了多线程同步的问题,代码如下:
public enum Singleton {
INSTANCE;
}
但是,这种方式的缺点也是很明显的,在日常的开发中,如果我们需要实例化一个单例类是,需要记住相应的获取方法,这无疑是给日常的开发增大了难度。
最后
当然,以上介绍的几种常见的单例模式,还有如静态内部类实现单例等。以上介绍有误的以及其他不足之处也希望大家多多指正。