使用饿汉式单例、带双重检查锁定机制的懒汉式单例以及IoDH技术实现负载均衡器 LoadBalancer

看前需知:
开始之前我们得知道什么是单例模式?单例模式可以简单理解为,确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法
单例模式主要三个要点:
1、是某个类只能有一个实例;
2、是它必须自行创建这个实例;
3、是它必须自行向整个系统提供这个实例。
在我们要知道饿汉式,懒汉式是单例模式的两种不同实现方式

1、饿汉式单例实现负载均衡器 LoadBalancer

由于在定义静态变量的时候实例化单例类,因此在类加载的时候就已经
创建了instance单例对象,当类被加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例将被创建。使用饿汉式单例来实现负载均衡器LoadBalancer类的设计,则不会出现创建多个单例对象的情况,可确保单例对象的唯一性

public class DadmashDesignMode {
    //当类被加载时,静态变量instance会被初始化,
    private static final DadmashDesignMode instance=new DadmashDesignMode();
    //服务器集合
    private List<String> serverList= null;

    //私有构造函数会被调用,单例类的唯一实例将被创建
    private DadmashDesignMode (){
        serverList = new ArrayList();
    }
    public static DadmashDesignMode getInstance(){
        return instance;
    }
    //增加服务器
    public void addService(String servver){
        System.out.println(servver);
        serverList.add(servver);
    }

    //删除服务器
    public void removeServer(String servver){
        serverList.remove(servver);
    }

    //使用Random类随机获取服务器
    public String getService(){
        Random random=new Random();
        //根据集合的大小来随机生成
        int i=random.nextInt(serverList.size());
        return (String) serverList.get(i);
    }

}

测试结果:

``
在这里插入图片描述
优缺点:饿汉式单例类在类被加载时就将自己实例化,它的优点在于无须考虑多线程访问问题,可以确保实例的唯一性;
缺点:无论系统在运行时是否需要使用该单例对象,由于在类加载时该
对象就需要创建,因此从资源利用效率角度来讲,浪费资源

2、双重检查锁定机制的懒汉式单例实现负载均衡器 LoadBalancer

public class LazybonesDesignMode {
    private volatile static LazybonesDesignMode instance = null;

    //服务器集合
    private List<String> serverList= null;

    private LazybonesDesignMode() {
        serverList = new ArrayList();

    }
    //synchronized为了处理多个线程同时访问的问题  但是这种方式会很影响系统的性能,效率不行,
    /*synchronized public static LazybonesDesignMode getInstance() {
        if (instance == null) {//判断是否为空,
            instance = new LazybonesDesignMode();
        }
        return instance;
    }*/
    //这种直接在方法需要的地方加synchronized

    /**
     * 用双重检查锁定来实现懒汉式单例类
     * 需要在静态成员变量instance之前增加修饰符volatile,被volatile修饰的成员变量可以确保多个线程都能够正确处理,且该代
     * 码只能在JDK 1.5及以上版本中才能正确执行。由于volatile关键字会屏蔽Java虚拟机所做的一
     * 些代码优化,可能会导致系统运行效率降低,因此即使使用双重检查锁定来实现单例模式也
     * 不是一种完美的实现方式。
     * @return
     */
    public static LazybonesDesignMode getInstance() {
        //第一重判断
        if (instance == null) {
            //锁定代码块
            synchronized (LazybonesDesignMode.class) {
                //第二重判断
                if (instance == null) {
                    instance = new LazybonesDesignMode(); //创建单例实例
                }
            }
        }
        return instance;
    }


    //增加服务器
    public void addService(String servver){
        System.out.println(servver);
        serverList.add(servver);
    }

    //删除服务器
    public void removeServer(String servver){
        serverList.remove(servver);
    }

    //使用Random类随机获取服务器
    public String getService(){
        Random random=new Random();
        //根据集合的大小来随机生成
        int i=random.nextInt(serverList.size());
        return (String) serverList.get(i);
    }


}

测试代码

 @Test
    public void LazybonesDesignModeTest(){
        ///创建四个LoadBalancer对象
        LazybonesDesignMode balancer1 ,balancer2,balancer3,balancer4;
        balancer1 = LazybonesDesignMode.getInstance();
        balancer2 = LazybonesDesignMode.getInstance();
        balancer3 = LazybonesDesignMode.getInstance();
        balancer4 = LazybonesDesignMode.getInstance();

        //判断服务器负载均衡器是否相同
        if(balancer1==balancer2&&balancer3==balancer4&&balancer1==balancer4){
            System.out.println("使用懒汉单例  ,,,服务器负载均衡器具有唯一性!");
        }
        //模拟增加服务器,
        balancer1.addService("Service1");
        balancer1.addService("Service 2");
        balancer1.addService("Service 3");
        balancer1.addService("Service 4");

        //模拟客户端请求的分发
        for (int i=0;i<10;i++){
            String service = balancer1.getService();
            System.out.println("分发请求至服务器:"+service);
        }
    }
  • 优缺点:
    懒汉式单例类在第一次使用时创建,无须一直占用系统资源,实现了延迟加载,但是必须处理好多个线程同时访问的问题,特别是当单例类作为资源控制器,在实例化时必然涉及资源初始化,而资源初始化很有可能耗费大量时间,这意味着出现多线程同时首次引用此类的机率变得较大,需要通过双重检查锁定等机制进行控制,这将导致系统性能受到一定影响

*注意:
使用双重检查锁定来实现懒汉式单例类,需要在静态成员变量instance之
前增加修饰符volatile,被volatile修饰的成员变量可以确保多个线程都能够正确处理,且该代码只能在JDK 1.5及以上版本中才能正确执行。由于volatile关键字会屏蔽Java虚拟机所做的一些代码优化,可能会导致系统运行效率降低,因此即使使用双重检查锁定来实现单例模式也不是一种完美的实现方式。*所以产生用IoDH技术实现负载均衡器 LoadBalancer

3、IoDH技术实现负载均衡器 LoadBalancer

IODH技术完美解决了饿汉式单例类不能实现延迟加载,不管将来用不用始终占据内存;懒汉式单例类线程安全控制烦琐,而且性能受影响。

public class IODHDesignMode {

    //服务器集合
    private List<String> serverList= null;

    private IODHDesignMode() {
        serverList = new ArrayList();
    }
    //内部类  第一次调用getInstance()时将加载内部类HolderClass
    private static class HolderClass {
        /**
         * 内部类中定义了一个static类型的变量
         * instance,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次,,,,,解决懒汉式的线程安全问题
         */
        private final static IODHDesignMode instance = new IODHDesignMode();
    }
    public static IODHDesignMode getInstance() {
        //需要时调用,解决饿汉式资源浪费问题
        return HolderClass.instance;
    }


    //增加服务器
    public void addService(String servver){
        System.out.println(servver);
        serverList.add(servver);
    }

    //删除服务器
    public void removeServer(String servver){
        serverList.remove(servver);
    }

    //使用Random类随机获取服务器
    public String getService(){
        Random random=new Random();
        //根据集合的大小来随机生成
        int i=random.nextInt(serverList.size());
        return (String) serverList.get(i);
    }

}

由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例化Singleton,第一次调用getInstance()时将加载内部类HolderClass,在该内部类中定义了一个static类型的变量instance,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。由于getInstance()方法没有任何线程锁定,因此其性能不会造成任何影响。
测试代码:

   @Test
    public void IODHDesignModeTest() {
        IODHDesignMode s1, s2;
        s1 = IODHDesignMode.getInstance();
        s2 = IODHDesignMode.getInstance();


        //判断服务器负载均衡器是否相同
        if(s1==s2){
            System.out.println("IODH 服务器负载均衡器具有唯一性!");
        }

        //模拟增加服务器,
        s1.addService("Service1");
        s1.addService("Service 2");
        s1.addService("Service 3");
        s1.addService("Service 4");

        //模拟客户端请求的分发
        for (int i=0;i<10;i++){
            String service = s1.getService();
            System.out.println("分发请求至服务器:"+service);
        }
    }

测试结果:
在这里插入图片描述通过使用IoDH,我们既可以实现延迟加载,又可以保证线程安全,不影响系统性能,不失为一种最好的Java语言单例模式实现方式

该讲得讲完了,有什么错误欢迎到下方留言,谢谢您

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值