1.定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容不一致等错误。
例如:windows的回收站,操作系统中的文件系统,多线程中的线程池,打印机的后台处理服务,应用程序的日志对象,数据库的连接池,网站的计数器,web应用的配置对象,应用程序的对话框?
单例模式的特点:
a.单例类只有一个实例对象;
b.该单例对象必须由单例类自行创建;
c.单例类对外提供一个访问该单例的全局访问点。
2.单例模式的结构与实现
单例模式是设计模式中最简单的模式之一,通常,普通类的构造函数是共公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的共有函数用于创建或获取该静态私有实例。
2.1 单例模式的结构
单例模式的主要角色如下:
2.2 单例模式的实现方式
步骤其实都是3步:
1.首先他不能被外部类实例化,所以构造方法必须用private关键字,第一步就是先写一个私有的构造方法。
private Singleton(){ };
2.因为只能自己实例化自己,所以,要自己创建一个对象。以下哪个都可以,一个属于懒汉式,一个属于饿汉式。
private static Singleton instance; //懒汉式
private static HungrySingleton instance = new Singleton(); //饿汉式
3.要想外部访问到这个对象,那么必须定义一个public方法,这样把自己创建的对象传进去,让外部类使用才可以。
public static Singleton getInstance()
{ return instance; }
第一种,懒汉:该模式的特点是因为比较懒,类加载时没有生成单例,只有当第一次调用getInstance方法时才去创建这个单例。代码如下:
线程不安全的:因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。 public class LazySingleton { private LazySingleton(){ } private static LazySingleton instance; public static LazySingleton getInstance(){ if(instance == null){ instance = new LazySingleton(); } return instance; } }
描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。 优点:第一次调用才初始化,避免内存浪费。 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。 getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。 public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
第二种,饿汉式:直接创建对象,不管你有没有用,反正提前创建好了。就和怕饿着一样,先准备好吃的再说。
public class HungrySingleton { private HungrySingleton(){ } private static HungrySingleton instance = new HungrySingleton(); public static HungrySingleton getInstance(){ return instance; } }
测试以上的是不是同一个对象:结果显示对象地址都一样,是同一个。
import org.testng.annotations.Test; public class TestModel { @Test public void test() { HungrySingleton hungrySingleton = HungrySingleton.getInstance(); HungrySingleton hungrySingleton1 = HungrySingleton.getInstance(); System.out.println(hungrySingleton); System.out.println(hungrySingleton1); System.out.println(hungrySingleton == hungrySingleton1); assert hungrySingleton == hungrySingleton1; } @Test public void test1() { LazySingleton lazySingleton = LazySingleton.getInstance(); LazySingleton lazySingleton1 = LazySingleton.getInstance(); System.out.println(lazySingleton); System.out.println(lazySingleton1); System.out.println(lazySingleton == lazySingleton1); assert lazySingleton == lazySingleton1; } }
第三种:双检锁/双重校验锁(DCL,即 double-checked locking)
描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。 getInstance() 的性能对应用程序很关键。 public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
其他的参考:https://www.runoob.com/design-pattern/singleton-pattern.html