设计模式之单例模式

单例模式的定义

单例模式就是确保某一个类只有一个实例,并且提供一个全局访问点。

单例模式的常用

1.Windows的任务管理器

2.Windows的回收站,也是一个单例应用

3.项目中的读取配置文件的对象

4.数据库的连接池

5.Servlet中的Application Servlet

6.Spring中的Bean默认也是单例的

7.SpringMVC Struts中的控制器

单例模式的分类

饿汉式

线程安全 调用率高 但是不能延迟加载

懒汉式

线程安全 调用率不高 但是可以延迟加载

代码说明

饿汉式

public class Singleton {
    // 提供静态的全局变量,作为访问该类的实例入口
    private static Singleton singleton = new Singleton();

    /**
     * 构造器私有,无法创建对象
     */
    private Singleton() {

    }

    /**
     * 对外提供get方法获取该类的实例
     * @return
     */
    public static Singleton getSingleton() {
        return singleton;
    }
}

懒汉式

public class Singleton {

    // 提供静态的全局变量,作为访问该类的实例入口
    private static  Singleton singleton = null;

    /**
     * 构造器私有,无法创建对象
     */
    private Singleton() {
        System.out.println("构造函数被调用了");
    }

    /**
     * 对外提供get方法获取该类的实例
     * @return
     */
    public static Singleton getSingleton() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

当全局变量初始化放到了实例化方法中,延迟产生对象。 当多个线程统一访问时,有可能出现线程不安全的情况。

解决方案A

只要getInstance()加上同步锁,,一个线程必须等待另外一个线程创建完后才能使用这个方法,这就保证了单利的唯一性。
加锁会导致synchronized修饰的同步块可是要比一般的代码慢上几倍的!如果存在很多次的getInstance()调用,那性能问题就不得不考虑了。

public class Singleton {

    // 提供静态的全局变量,作为访问该类的实例入口
    private static  Singleton singleton = null;

    /**
     * 构造器私有,无法创建对象
     */
    private Singleton() {
        System.out.println("构造函数被调用了");
    }

    /**
     * 对外提供get方法获取该类的实例
     * @return
     */
    public synchronized static Singleton getSingleton() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

解决方案B

这种模式叫做双重校验锁式分别在代码锁前后进行判空校验 ,双重校验锁式是线程安全的。
 

在JDK 5以后开始提供volatile关键字修饰变量。就可以实现线程安全的延迟初始化,这样的话,重排序就是会被禁止,在多线程的时候,CPU也有共享内存,我们加上了这个关键字了之后,所有线程就能看到共享内存的最新状态,保证了内存的可见性。


public class Singleton {

    //提供静态的全局变量 作为访问该类实例的入口 但是这里不立即加载
    private volatile  static Singleton sh = null;


    /**
     * 构造器私有 无法创建对象
     */
    private Singleton(){
        System.out.println("被调用了");
    }

  
    public static Singleton getInstance() {
        if(sh == null){
            synchronized(Singleton.class){
                if(sh == null){
                    sh = new Singleton();
                }
            }
        }
        return sh;
    }
}

解决方案C

然而,如果没有在Jdk1.5之后,怎么办?
使用静态内部类的方式
 

public class Singleton {
        private static class InnerClass {
            private static Singleton staticInnerClassSingleton = new Singleton();
        }

        public static Singleton getInstance() {
            return InnerClass.staticInnerClassSingleton;
        }
        private Singleton(){

        }
}

 

单例模式的优缺点

优点:

        节约了系统资源。由于系统中只存在一个实例对象,对与一些需要频繁创建和销毁对象的系统而言,单例模式无疑节约了系统资源和提高了系统的性能。             
        单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。   

缺点:

由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。       

单例类的职责过重,在一定程度上违背了“单一职责原则”。

使用场景

        系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。             

        客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
 

总结

单例模式中确保一个类最多只有一个实例,构造器私有,而且必须要提供实例的全局访问点,单例模式可能会因为多线程的问题带来的安全隐患。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值