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

单例模式

确保某个类只有一个实例,并且自行实例化向系统中提供这个实例,这个类被称为单例类,它提供全局访问的方法,是一种创建型模式。
特点:
(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)客户端调用类的单个实例只允许一个公共的访问节点,除了该访问节点,不能通过其他节点访问该实例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值