设计模式之(一)单例模式

    技术2026-06-06  6

    单例模式

    确保某个类只有一个实例,并且自行实例化向系统中提供这个实例,这个类被称为单例类,它提供全局访问的方法,是一种创建型模式。 特点: (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; } }

    案例

    一.负载均衡设计

    package com.learn.designmode.mode.singleLeton; import java.util.ArrayList; import java.util.List; import java.util.Random; /** * 模拟负载均衡设计,模拟随机向服务器转发客户端请求 */ public class LoadBalance { /** * 服务器集群 */ private List<String> serverList; /** * 私有变量存储唯一实例 */ private static LoadBalance loadBalance; // 私有静态构造方法实例化 private LoadBalance(){ serverList = new ArrayList<>(); } public static LoadBalance getInstance(){ if (loadBalance == null){ loadBalance = new LoadBalance(); } return loadBalance; } // 删除服务器 private void deleteServer(String server){ serverList.remove(server); } // 增加服务器 public void addServer(String server){ serverList.add(server); } // 随机转发服务器 public String getServer(){ Random random = new Random(); int index = random.nextInt(serverList.size()); return serverList.get(index); } } package com.learn.designmode.mode.singleLeton.test; import com.learn.designmode.mode.singleLeton.LoadBalance; public class LoadBalanceTest { public static void main(String[] args) { LoadBalance loadBalance = LoadBalance.getInstance(); LoadBalance loadBalance1 = LoadBalance.getInstance(); LoadBalance loadBalance2 = LoadBalance.getInstance(); LoadBalance loadBalance3 = LoadBalance.getInstance(); if (loadBalance == loadBalance1 && loadBalance1 == loadBalance2 & loadBalance2 == loadBalance3){ System.out.println("服务器负载均衡具有唯一性"); } loadBalance.addServer("server1"); loadBalance.addServer("server2"); loadBalance.addServer("server3"); loadBalance.addServer("server4"); for (Integer i = 0;i<= 10;i++){ System.out.println("转发服务器" + loadBalance.getServer()); } } }

    二.饿汉式单例和懒汉式单例的讨论

    以上作为简单的单例模式的使用,但是在高并发的情况下会发生异常,原因在于第一次调用getInstance方法时,instance为null,将执行new LoadBalance的操作,在此过程中需要大量的初始化工作,需要一定的时间创建自己的实例化对象,如果此时再调用一次getInstacne方法 判断条件

    if (loadBalance == null){ loadBalance = new LoadBalance(); }

    将被再次执行,导致创建了多个instance实例,违背了单例模式的初衷,导致运行错误。

    (1)饿汉式单例模式

    饿汉式单例模式是最简单的单例类结构图如下

    package com.learn.designmode.mode.singleLeton; public class EagerSingleton { // 将自身成员变量私有静态常量化 在类加载的时候初始化自身实例 private static final EagerSingleton eagerSingleton= new EagerSingleton(); private EagerSingleton(){ } // public static EagerSingleton getInstance(){ return eagerSingleton; } }

    (2)懒汉式单例与线程锁定

    懒汉式单例用到了延迟加载的技术,在类加载的时候不实例化对象,在需要的时候实例化,但是为了避免多个线程同时调用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; }

    饿汉式单例模式和懒汉式单例模式的比较

    一种更好的单例模式(静态内部类)

    package com.learn.designmode.mode.singleLeton; public class Singleton { private static class HolderClass{ private static Singleton singleton = new Singleton(); } public static Singleton getInstance(){ return HolderClass.singleton; } }

    单例模式总结

    主要优点

    (1)单例模式提供了唯一实例的受控访问,因为单例类封装了它唯一实例,所以它可以严格控制客户以及如何访问它。 (2)由于在系统中只存在一个对象,因此可以节约资源,对于一些需要频繁创建和销毁的对象,单例模式无疑提高了性能。 (3)允许可变的数目,基于单例模式,开发人员可进行扩展,使用与控制单例对象相似的方法来获得指定个数的实例对象,既节省资源,有解决了由于单例模式共享过多产生的问题。

    主要缺点

    (1)由于单例模式没有抽象层,因此单例类的扩展很大难度 (2)单例类的职责过重,在一定程度上违背了单一原则,单例类又提供了业务方法,又提供了创建对象的方法,将对象的创建于功能耦合在一起 (3)很多面向对象设计语言都提供了垃圾回收机制,因此实例化共享长时间不被利用,系统会认为它是垃圾,会自动回收并销毁资源,下次利用又被实例化,导致共享的单例对象状态丢失。

    使用场景

    (1)系统只需要一个实例对象,比如唯一的序列号生成器或者资源管理器,或者考虑资源消耗太大而只允许创建一个实例 (2)客户端调用类的单个实例只允许一个公共的访问节点,除了该访问节点,不能通过其他节点访问该实例。

    Processed: 0.016, SQL: 10