确保某个类只有一个实例,并且自行实例化向系统中提供这个实例,这个类被称为单例类,它提供全局访问的方法,是一种创建型模式。 特点: (1)自行实例化(公有静态方法new 实例) (2)只有一个实例(构造方法设为私有,不让new实例,并且静态方法进行空指针检验) (3)必须自行的向系统中提供这个实例(公有静态方法返回) 简单版单例模式
/** * 演示单例模式 任务管理器 */ public class TaskManager { private TaskManager taskManager = null; // 这里要可见性为私有 防止被new一个实例 private TaskManager(){ } private void displayTask(){ System.out.println("进程1"); } // 静态方法,无须 public static TaskManager getInstance(){ if (taskManager == null){ taskManager = new TaskManager(); } return taskManager; } }以上作为简单的单例模式的使用,但是在高并发的情况下会发生异常,原因在于第一次调用getInstance方法时,instance为null,将执行new LoadBalance的操作,在此过程中需要大量的初始化工作,需要一定的时间创建自己的实例化对象,如果此时再调用一次getInstacne方法 判断条件
if (loadBalance == null){ loadBalance = new LoadBalance(); }将被再次执行,导致创建了多个instance实例,违背了单例模式的初衷,导致运行错误。
饿汉式单例模式是最简单的单例类结构图如下
package com.learn.designmode.mode.singleLeton; public class EagerSingleton { // 将自身成员变量私有静态常量化 在类加载的时候初始化自身实例 private static final EagerSingleton eagerSingleton= new EagerSingleton(); private EagerSingleton(){ } // public static EagerSingleton getInstance(){ return eagerSingleton; } }懒汉式单例用到了延迟加载的技术,在类加载的时候不实例化对象,在需要的时候实例化,但是为了避免多个线程同时调用getInstance方法,使用synchronized锁住getInstance方法
package com.learn.designmode.mode.singleLeton; public class LayzerSingleton { // 不作为常量进行实例化 private static LayzerSingleton layzerSingleton = null; private LayzerSingleton(){ } synchronized public static LayzerSingleton getInstance(){ if (layzerSingleton == null){ layzerSingleton = new LayzerSingleton(); } return layzerSingleton; } }此方式存在性能问题,现实中不需要锁住整个获取实例的方法,只需要锁住判断语句后的创建实例的操作。
// 改进后的懒汉式单例模式 public static LayzerSingleton getInstance1(){ if (layzerSingleton == null){ synchronized (LayzerSingleton.class){ layzerSingleton = new LayzerSingleton(); } } return layzerSingleton; }上述改进还是会出现多个实例的隐患,原因在于假设某一瞬间有两个线程A,B同时调用getInstance方法,均通过了空值校验的判读语句,由于有了加锁机制,当线程A进入synchronized锁定的代码中创建了实例,线程B处于等待状态,当线程A执行完毕后,线程B并不知道实例已经被创建,判断语句过掉执行创建实例,导致创建了多个实例,违背了单例模式的思想,因此需要一种**“双重检查锁定”**的方式
// 改进后的双重检验锁懒汉式单例模式 private static volatile LayzerSingleton layzerSingleton1= null; // 双重校验锁的懒汉式单例模式 public static LayzerSingleton getInstance2(){ if (layzerSingleton1 == null){ synchronized (LayzerSingleton.class){ if (layzerSingleton1 == null){ layzerSingleton1 = new LayzerSingleton(); } } } return layzerSingleton1; }(1)单例模式提供了唯一实例的受控访问,因为单例类封装了它唯一实例,所以它可以严格控制客户以及如何访问它。 (2)由于在系统中只存在一个对象,因此可以节约资源,对于一些需要频繁创建和销毁的对象,单例模式无疑提高了性能。 (3)允许可变的数目,基于单例模式,开发人员可进行扩展,使用与控制单例对象相似的方法来获得指定个数的实例对象,既节省资源,有解决了由于单例模式共享过多产生的问题。
(1)由于单例模式没有抽象层,因此单例类的扩展很大难度 (2)单例类的职责过重,在一定程度上违背了单一原则,单例类又提供了业务方法,又提供了创建对象的方法,将对象的创建于功能耦合在一起 (3)很多面向对象设计语言都提供了垃圾回收机制,因此实例化共享长时间不被利用,系统会认为它是垃圾,会自动回收并销毁资源,下次利用又被实例化,导致共享的单例对象状态丢失。
(1)系统只需要一个实例对象,比如唯一的序列号生成器或者资源管理器,或者考虑资源消耗太大而只允许创建一个实例 (2)客户端调用类的单个实例只允许一个公共的访问节点,除了该访问节点,不能通过其他节点访问该实例。
