目录
单例模式概述
简单概述:
单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。
单例模式有三个要点:
- 某个类只能有一个实例;
- 它必须自行创建这个实例;
- 它必须自行向整个系统提供这个实例。
单例模式的作用:
- 某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。(例如spring框架管理所有的javaBean对象,默认为单例模式节省实例化对象,交由springIOC容器管理项目中所有的javaBean对象,并提供了多例模式)
- 省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
- 有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程)
术语
- 单例:Singleton
单例模式的结构与实现
单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。
单例模式的实现
饿汉式(静态常量)
/** 饿汉式(静态常量)
*/
public class DBAccess {
// 构造器私有化,避免外部创建对象
private DBAccess() {
}
// static修饰,保障其能够被静态方法访问
private final static DBAccess dbAccess = new DBAccess();
// 外部直接调用静态方法实例化对象
public static DBAccess getInstance() {
return dbAccess;
}
}
饿汉式(静态代码块)
/**
* 饿汉式(静态代码块)
*/
class DBAccess2 {
private DBAccess2() {
}
private static DBAccess2 dbAccess = null;
static {
dbAccess = new DBAccess2();
}
public static DBAccess2 getInstance() {
return dbAccess;
}
}
懒汉式(线程不安全)
/**
* 懒汉式(线程不安全)
*/
class DBAccess3 {
private DBAccess3() {
}
private static DBAccess3 dbAccess = null;
public static DBAccess3 getInstance() {
if (dbAccess == null) {
dbAccess = new DBAccess3();
}
return dbAccess;
}
}
懒汉式(线程安全,同步代码块)
/**
* 懒汉式(同步代码块)
*/
class DBAccess4 {
private DBAccess4() {
}
private static DBAccess4 dbAccess = null;
public static DBAccess4 getInstance() {
synchronized (DBAccess4.class) {
if (dbAccess == null) {
dbAccess = new DBAccess4();
}
}
return dbAccess;
}
}
懒汉式(线程安全,同步方法)
/**
* 懒汉式(线程安全,同步方法)
*/
class DBAccess5 {
private DBAccess5() {
}
private static DBAccess5 dbAccess = null;
public synchronized static DBAccess5 getInstance() {
if (dbAccess == null) {
dbAccess = new DBAccess5();
}
return dbAccess;
}
}
双重检查
/**
* 双重检查
*/
class DBAccess6 {
private DBAccess6() {
}
private static DBAccess6 dbAccess = null;
public static DBAccess6 getInstance() {
if (dbAccess == null) {
synchronized (DBAccess6.class) {
if (dbAccess == null) {
dbAccess = new DBAccess6();
}
}
}
return dbAccess;
// return new DBAccess6();
}
}
静态内部类
/**
* 静态内部类
*/
class DBAccess7 {
private DBAccess7() {
}
private static class DBAccess7Instance{
private static DBAccess7 dbAccess = new DBAccess7();
}
public static DBAccess7 getInstance() {
return DBAccess7Instance.dbAccess;
}
}
枚举
/**
* 枚举
*/
enum DBAccess8{
DBACCESS;
public static DBAccess8 getInstance() {
return DBAccess8.DBACCESS;
}
}
饿汉式:该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。
饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。懒汉式:该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。
注意:如果编写的是多线程程序,则不要删除上例代码中的关键字synchronized,否则将存在线程非安全的问题。如果不删除这个关键字就能保证线程安全。
双重检查:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。getInstance() 的性能对应用程序很关键静态内部类:该模式使用匿名内部类的方法,当实力外部类时,内部类不会被实例化,
只有在调用内部类的时候才会实例化加载资源,能省则省。
枚举:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
结论:
单例中两种饿汉式可用,但是存在性能问题
单例中三种懒汉式不推荐,存在线程安全问题,同步方法的方式解决了线程的问题,但是性能极差
最后三种单例模式值得推荐
总结
1.主要优点
单例模式的主要优点如下:
- 单例模式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。
- 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
- 允许可变数目的实例。基于单例模式我们可以进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例,既节省系统资源,又解决了单例单例对象共享过多有损性能的问题。
2.主要缺点
单例模式的主要缺点如下:
- 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
- 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
- 现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失。
3.适用场景
在以下情况下可以考虑使用单例模式:
- 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。
- 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
至此,单例模式介绍完毕,由于作者水平有限难免有疏漏,欢迎留言纠错。