一、单例模式
在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于协调系统整体的行为并且避免了创建大量实例对象而带来的内存浪费。
1、优点:
- 在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例,简化对象实例的管理。
- 提供了对唯一实例的受控访问。
- 由于在系统内存中只存在一个对象,因此可以节约系统资源,当需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
2、缺点:
- 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
- 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
- 单例类的职责过重,在一定程度上违背了“单一职责原则”。
3、应用场景举例:
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源(单例指的是数据库连接池对象)。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为使用单例模式来维护,就可以大大降低这种损耗。
- 多线程的线程池的设计一般也是采用单例模式(线程池对象),这是由于线程池要方便对池中的线程进行控制。
- Spring中对各个Bean的管理采用的也是单例。
4、代码实现
代码实现单例模式一般常用的有双重检测法以及使用枚举来实现单例,下面将分别介绍这两种方式实现的单例模式。
(1) 双重检测法
使用双重检测法其实并未能完全实现单例模式,比如通过反射技术还是能够创建出多个实例的。此时可以通过枚举法来避免这种情形。
public final class Resource {
/**
* 实现单例模式时,构造函数需要设置成为private
*/
private Resource() {
}
/**
* 使用volatile关键字保证每个线程得到的Instance实例是最新的
*/
private static volatile Resource Instance = null;
/**
* 保证在Instance不为null时不需要进行额外的加锁,使用volatile关键字来保证各个
* 线程在获取单例时得到的是最新数据从而防止多次初始化实例对象
*/
public static Resource getInstance() {
if (Instance == null) {
synchronized (Resource.class) {
if (Instance == null) {
Instance = new Resource();
}
}
}
return Instance;
}
}
(2) 枚举法
在枚举中,每个实例其实都是public static final类型的,每个实例对象只会被初始化一次,因为不必担心多线程环境下的安全问题。
public enum Resource {
/**
* 枚举类的实例都是static final修饰的,因此只会被出初始化一次,
* 不必担心多线程环境下的安全问题
*/
INSTANCE;
Resource() {
}
public void action(int code) {
switch (code) {
case 1:
System.out.println("资源使用");
break;
case 2:
System.out.println("资源修改");
break;
default:
System.out.println("无处理");
break;
}
}
public static void main(String[] args) {
Resource.INSTANCE.action(1);
}
}