GOF之单例模式(创建型模式) ✨每日积累

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
简介: GOF之单例模式(创建型模式) ✨每日积累

单例模式是什么


创造型设计模式,负责自己创建自己的对象,构造器私有化,不允许外界直接new自己,确保每一次都只有单个对象被创建,这个类提供了一种访问自身对象的一个入口,可以直接访问。优点是可以在内存中只有一个实例,减少了内存的开销,尤其是频繁创建和销毁实例;还有对资源的多重占用(例如写io操作)。缺点就是不能继承,没有接口,与单一职责冲突,一个类只关心自己创建自己对象的逻辑,不关心外面如何实例化。


何时使用单例模式

当整个软件系统中,对某个类只能存在一个实例对象的情况下我们可以使用单例模式。具体使用那种单例,需要结合业务场景。


单例模式三个特点

1、单例类只能有一个实例。

2、单例类必须自己创建自己的唯一实例。

3、单例类必须给所有其他对象提供这一实例。

JDK中Runtime使用单例模式

饿汉式

1.png

单例模式实现的几种方式

1、懒汉试

/**
 * 懒加载,第一次调用才初始化,避免内存浪费
 */
public class TestLazySingleton {
    private static TestLazySingleton testLazySingleton;
    private TestLazySingleton(){}
    //线程不安全,单线程正常工作,多线程情况下并非单例
    public static TestLazySingleton getInstance(){
        if (testLazySingleton == null) return testLazySingleton = new TestLazySingleton();
        return testLazySingleton;
    }
    //线程安全,加锁,效率低
    public static synchronized TestLazySingleton getInstanceSafe(){
        if (testLazySingleton == null) return testLazySingleton = new TestLazySingleton();
        return testLazySingleton;
    }
}

2、饿汉试

/**
 * 饿汉试:比较常用,非懒加载,项目启动即加载(非使用才加载),浪费内存容易产生垃圾对象。
 * 它基于 classloader 机制避免了多线程的同步问题
 */
public class TestHungrySingleTon{
    private static TestHungrySingleTon testHungrySingleTon = new TestHungrySingleTon();
    private TestHungrySingleTon(){}
    public static TestHungrySingleTon getInstance(){
        return testHungrySingleTon;
    }
}

3、双重锁/校验机制

/**
 * 双重校验机制:懒加载机制,线程安全,可在多线程保持高性能
 */
public class TestDoubleCheckedLocking{
    //此处使用volatile是防止类初始化是发生指令重排,导致对象初始化半成品对象就被其他线程使用了
    private volatile static TestDoubleCheckedLocking testDoubleCheckedLocking;
    private TestDoubleCheckedLocking (){}
    public static TestDoubleCheckedLocking getSingleton() {
        if (testDoubleCheckedLocking == null) {
            synchronized (TestDoubleCheckedLocking.class) {
                if (testDoubleCheckedLocking == null) {
                    testDoubleCheckedLocking = new TestDoubleCheckedLocking();
                }
            }
        }
        return testDoubleCheckedLocking;
    }
}

4、登记式/静态内部类

/**
 * 登记式/静态内部类:懒加载,节省内存。线程安全,利用了 classloader 机制来保证初始化 instance 时只有一个线程。
 */
public class TestRegistrationSingleton{
    private TestRegistrationSingleton(){}
    private static class GetRegistrationSingletonUtil{
        private static final TestRegistrationSingleton SINGLETON_INSTANCE = new TestRegistrationSingleton();
    }
    public static final TestRegistrationSingleton getInstance(){
        return GetRegistrationSingletonUtil.SINGLETON_INSTANCE;
    }
}

5、枚举

public enum TestEnumSingleton {
    INSTANCE;
    public void whateverMethod() {
    }
    public static void main(String[] args) {
        TestEnumSingleton testEnumSingleton = TestEnumSingleton.INSTANCE;
        TestEnumSingleton testEnumSingletonV2 = TestEnumSingleton.INSTANCE;
        CurrentExistingSingleton currentExistingSingleton = CurrentExistingSingleton.SingletonEnum.SINGLETON.getInstance();
        CurrentExistingSingleton currentExistingSingletonV2 = CurrentExistingSingleton.SingletonEnum.SINGLETON.getInstance();
        //都是true
        System.out.println(testEnumSingleton == testEnumSingletonV2);
        System.out.println(currentExistingSingleton == currentExistingSingletonV2);
    }
}
/**
 * 对现存类的加入单例功能的扩展
 */
class CurrentExistingSingleton {
    private CurrentExistingSingleton() {
    }
    public static enum SingletonEnum {
        SINGLETON;
        private CurrentExistingSingleton instance = null;
        private SingletonEnum() {
            instance = new CurrentExistingSingleton();
        }
        public CurrentExistingSingleton getInstance() {
            return instance;
        }
    }
}

使用场景

一般情况下,不建议使用第 1 种懒汉方式,建议使用第 2 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 4 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第5 种枚举方式。如果有其他特殊的需求,可以考虑使用第 3 种双检锁方式。


单例模式实践

参考https://blog.csdn.net/lovelion/article/details/7420885文章


实践负载均衡器的设计与实现

将负载均衡器LoadBalancer设计为单例类,其中包含一个存储服务器信息的集合serverList,每次在serverList中随机选择一台服务器来响应客户端的请求

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* 使用单例实现简单的负载均衡功能
*/
public class TestLoadBalancer {
   //懒加载模式
   private static TestLoadBalancer testLoadBalancer;
   //服务器集合
   private List serverList;
   //私有化构造,禁止外界直接new实例
   private TestLoadBalancer(){
       serverList = new ArrayList();
   }
   /**
    * 使用枚举:在SingletonEnum中定义了枚举类型的实例对象Singleton,
    * 再按照单例模式的要求在其中定义一个Singleton类型的对象instance,其初始值为null;
    * 我们需要将SingletonEnum的构造函数改为私有的,在私有构造函数中创建一个Singleton的实例对象;
    * 最后在getInstance()方法中返回该对象。
    */
   public static enum SingletonEnum{
       SINGLETON;
       private SingletonEnum() {
           testLoadBalancer = new TestLoadBalancer();
       }
       public TestLoadBalancer getInstance() {
           return testLoadBalancer;
       }
   }
   //增加服务器
   public void addServer(String server) {
       serverList.add(server);
   }
   //删除服务器
   public void removeServer(String server) {
       serverList.remove(server);
   }
   //使用Random类获取服务器
   public String getServer(){
       Random random = new Random();
       int i = random.nextInt(serverList.size());
       return String.valueOf(serverList.get(i));
   }
   public void testSimulationClient(){
       TestLoadBalancer clientV1 = SingletonEnum.SINGLETON.getInstance();
       TestLoadBalancer clientV2 = SingletonEnum.SINGLETON.getInstance();
       TestLoadBalancer clientV3 = SingletonEnum.SINGLETON.getInstance();
       TestLoadBalancer clientV4 = SingletonEnum.SINGLETON.getInstance();
       TestLoadBalancer clientV5 = SingletonEnum.SINGLETON.getInstance();
       //判断服务器是否一致
       if (clientV1 == clientV2 &&  clientV2 == clientV3 && clientV3 == clientV4 && clientV4 == clientV5){
           System.out.println("服务器负载均衡器具有唯一性!");
       }
       //增加服务器
       clientV1.addServer("Server 1");
       clientV1.addServer("Server 2");
       clientV1.addServer("Server 3");
       clientV1.addServer("Server 4");
       //模拟客户端请求的分发
       for (int i = 0; i < 10; i++) {
           String server = clientV1.getServer();
           System.out.println("分发请求至服务器: " + server);
       }
   }
   public static void main(String[] args) {
       new TestLoadBalancer().testSimulationClient();
   }
}
相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
6月前
|
设计模式 安全 Java
小谈设计模式(22)—单例模式
小谈设计模式(22)—单例模式
|
设计模式 Java
Java设计模式解析:工厂模式的奥秘
当谈论Java设计模式时,"工厂模式"无疑是一个不容忽视的重要主题。在本文中,我们将深入探讨工厂模式,探索它的概念、应用场景以及在Java中的具体实现。
73 0
|
6月前
|
设计模式 Java Spring
|
设计模式 SQL 安全
重学设计模式-单例模式
  单例模式是指一个类在整个程序运行中只允许存在一个实例,也就是说在JVM里面只存在一个实例,单例模式应用十分广泛,比如说一个公司里面只有一个CEO,一个家庭里面只有一个爸爸(当然,排除那些意外),单例模式主要应用在需要频繁使用创建和使用的一些类上面,因为只存在一个实例,所以节省了内存的开销,所有线程共享同一个实例,试想一下,如果一个类使用十分频繁,没有使用单例模式的情况下,一个线程需要创建一个实例,那么系统中将会出现出现很多多余的实例,对内存的消耗也很大,JVM中容易发生GC,比如数据库连接池,某些不太常用的对象,皆可使用单例模式来做,有助于提高系统的可用性。
66 0
|
设计模式 安全 Java
java设计模式之单例设计模式的妙用
1.设计模式 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美地解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因
84 1
|
设计模式
GOF之工厂模式(创建型模式) ✨ 每日积累
GOF之工厂模式(创建型模式) ✨ 每日积累
GOF之工厂模式(创建型模式) ✨ 每日积累
GOF之抽象工厂模式(创建型模式) ✨ 每日积累
GOF之抽象工厂模式(创建型模式) ✨ 每日积累
GOF之抽象工厂模式(创建型模式) ✨ 每日积累
GOF之装饰器模式(结构型模式) ✨ 每日积累
GOF之装饰器模式(结构型模式) ✨ 每日积累
GOF之装饰器模式(结构型模式) ✨ 每日积累
GOF之适配器模式(结构型模式) ✨ 每日积累
GOF之适配器模式(结构型模式) ✨ 每日积累
GOF之适配器模式(结构型模式) ✨ 每日积累
GOF之建造者模式(创建型模式)附图详解 ✨ 每日积累
GOF之建造者模式(创建型模式)附图详解 ✨ 每日积累
GOF之建造者模式(创建型模式)附图详解 ✨ 每日积累