本文为joshua317原创文章,转载请注明:转载自joshua317博客 Java设计模式(一)-单例模式 - joshua317的博客
一、单例模式介绍
单例模式(Singleton Pattern) 是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。类的构造函数是私有的,并且具有自身的静态实例。单例类一般情况只想内部保留一个实例对象,所以会选择将构造函数声明为私有的,这才使得单例类无法被继承。
1.1 单例模式的核心要素
(1)单例类只能有一个实例。
(2)单例类必须自己创建自己的唯一实例。
(3)单例类必须给所有其他对象提供这一实例。
1.2 单例模式的应用及实现说明
(1)意图: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
(2)主要解决: 一个全局使用的类频繁地创建与销毁。
(3)何时使用: 控制实例数目,节省系统资源的时候。
(4)如何解决: 判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
(5)关键代码: 构造函数是私有的;提供一个获得该实例的对外方法。
1.3 单例模式实现的思路
(1)一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);
(2)当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;
(3)同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
1.4 单例模式优缺点
优点:
(1)在内存里只有一个实例,所以能够节约系统资源,减少了内存的开销,尤其是频繁的创建和销毁实例,可以提高系统效率,同时也能够严格控制客户对它的访问。
(2)避免对资源的多重占用。
缺点:
也正是因为系统中只有一个实例,这样就导致了单例类的职责过重,违背了“单一职责原则”,同时也没有抽象类,这样扩展起来有一定的困难。
1.5 单例模式线程安全问题
单例模式 在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了 单例模式 中实例唯一的原则。 解决这个问题的办法是 为类是否已经实例化的变量提供一个互斥锁 (虽然这样会降低效率)。
二、单例模式应用场景
(1)无状态工具类:这种工具类不需要记录状态,只保证正确的应用就行,可以通过单例模式来定义。比如项目中用于读取配置文件的类
(2)数据共享:即多个不相关的两个线程或者进程之间实现通信。因为是一个实例,如果它的属性或者变量值被修改,所有引用都是同时修改的,当然需要 volatile 来定义变量。比如网站的计数器。
(3)日志应用:通常应用会向日志文件写日志信息,为了实时向文件写,通常会使用单例模式,保证有一个实例持有文件,然后进行操作。
(4)数据库连接池:数据库连接是一种数据库资源,使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,通过单例模式来维护,就可以大大降低这种损耗。
(5)Web应用的配置对象:读取文件需要消耗时间,如果读取大文件,消耗的时间和资源更久,所以通过单例模式可以大大降低消耗。
(6)生产唯一序列号:当系统需要持续不断地产生唯一序列号时,为避免频繁创建对象,所以通过单例模式可以大大降低消耗。
(7)Spring中,每个Bean
默认都是单例的,这样便于Spring容器进行管理
三、单例模式实现方式
常见的单例模式实现方式有五种:饿汉式
、懒汉式
、双重检测锁式
、静态内部类式
和枚举单例
。而在这五种方式中饿汉式
和懒汉式
又最为常见。
3.1 单例模式实现一:饿汉式
饿汉式:线程安全,调用效率高,但是不能延时加载。由于该模式在加载类的时候对象就已经创建了,所以加载类的速度比较慢,但是获取对象的速度比较快,且是线程安全的。这种方式比较常用,但容易产生垃圾对象。优点就是没有加锁,执行效率会提高。缺点是类加载时就初始化,浪费内存。它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到延迟加载的效果。
/**
* 单例模式实现一-饿汉式
*/
class Singleton1 {
/**
* 1.直接创建对象,定义静态属性,类初始化时,立即加载这个对象
*/
private static Singleton1 instance = new Singleton1