本文介绍23种设计模式之单例模式。
单例(Singleton)模式是Java中最简单的设计模式之一。这种类型的设计模式属于创建模式,因为此模式提供了创建对象的最佳方法之一。这种模式涉及一个类,它负责创建一个对象,同时确保只创建一个对象。这个类提供了一种方法来访问它的唯一对象,可以直接访问,而不需要实例化类的对象。
要点
单例模式的要点有三个:
1. 是某个类只能有一个实例;
2. 是它必须自行创建这个实例;
3. 是它必须自行向整个系统提供这个实例。
从实现角度来说,就是以下三点:
1. 是单例模式的类只提供私有的构造函数;
2. 是类定义中含有一个该类的静态私有对象;
3. 是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
单例模式的优点
- 在内存中只有一个对象,节省内存空间。
- 避免频繁的创建销毁对象,可以提高性能。
- 避免对共享资源的多重占用。
- 可以全局访问。
1、window的任务管理器就是很典型的单例模式,你肯定不能同时打开两个任务管理器;
2、数据库连接池技术一般采用的都是单例模式,因为数据库连接是一种数据库资源。系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的 效率损耗,这种效率上的损耗还是非常昂贵的,用单例模式来维护,就可以大大降低这种损耗;
3、我们在进行开发时对于配置文件的读取一般也是采用单例模式,因为配置文件中的内容是全局共享的资源;
4、多线程的线程池设计一般也要考虑单例模式,线程池能方便对池中线程的控制;
5、网站的统计类信息,一般也是采用单例模式,否则难以同步控制,例如统计我的博客的访问量;
6、我们开发应用程序的日志功能也是采用的单例模式,因为我们只能有一个实例去追加日志信息,否则不好控制。
java中单例模式是一种常见的设计模式,单例模式的实现有好几种,这里主要介绍两种:饿汉式单例、懒汉式单例。一种是懒汉式单例。饿汉式单例在单例类被加载时候,就实例化一个对象交给自己的引用;而懒汉式在调用取得实例方法的时候才会实例化对象。
在java中,饿汉式单例要优于懒汉式单例,C++中则一般使用懒汉式单例。由于饿汉式单例和懒汉式单例的构造方法是private的,所以他们都是不可继承的,但是其他很多单例模式是可以继承的,例如登记式单例。
懒汉式单例
当类被加载的时候该类就已经将自己的实例创建,这也是空间换取时间的典型应用,即我们在类加载的时候就实例化了这个对象,占用了内存空间,但是我们在用到这个对象的时候就不用去实例化了,直接拿去用就可以了,也就节省可时间,这也就是空间换取时间。
代码:
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
private Singleton() {}
private static Singleton single=null;
//静态工厂方法
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全。
1、在getInstance方法上加同步锁机制
public class Singleton {
private static Singleton s = null;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(s == null){
s = new Singleton();
}
return s;
}
}
2、双重检查锁定但是在多线程并发访问的情况下,每个线程每次获取实例都要判断下锁,效率比较低,为了提高效率,采用双重判断的方法,解决了效率的问题;
public class Singleton {
private static Singleton s = null;
private Singleton(){}
public static Singleton getInstance(){
/*如果第一个线程获取到了单例的实例对象,
* 后面的线程再获取实例的时候不需要进入同步代码块中了*/
if(s == null){
//同步代码块用的锁是单例的字节码文件对象,且只能用这个锁
synchronized(Singleton.class){
if(s == null){
s = new Singleton();
}
}
}
return s;
}
}
3、静态内部类这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。
public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
饿汉式单例
在类加载的时候并没有创建本类的实例对象,而是在其他类在第一次调用的时候才去创建。这也是典型的时间换取空间的应用,即类加载的时候没有创建对象,节省了内存,也就是节省了空间,调用的时候需要判断一下这个类的实例对象是否存在,浪费了时间,这也就是时间换取空间。
代码:
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
private Singleton1() {}
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return single;
}
}
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的。