设计模式---单例模式

概念

单例就是指整个程序中该类的实例仅有一个,并且该类自己负责创建自己的实例并向整个系统提供这个实例。
单例模式用来保证一个类仅有一个实例并提供访问它的全局访问点。主要解决一个全局使用的类频繁地创建和销毁。用来控制实例数目,节省系统资源。

比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个SessionFactory就行,这就会使用到单例模式。

分类

  • 饿汉式
  • 懒汉式(不推荐)
  • 双重检查
  • 静态内部类
  • 枚举
饿汉式

示例1【饿汉式写法-静态变量的方式】:

//饿汉式(静态变量)
class Singleton {
    //构造函数私有化,不能在外部new单例模式对象
    private Singleton(){}

    //静态变量存储单例类的唯一一个实例对象
    private final static Singleton instance = new Singleton();

    //对外提供一个方法,获取该类的对象
    public static Singleton getInstance() {
        return instance;
    }
}

优点

  • 写法简单,在类装载的时候就能完成实例化,避免了线程同步问题(即保证线程安全

缺点

  • 在类装载的时候就完成实例化,没有达到Lazy Loading(懒加载)的效果。如果从开始至终这个类未使用过,则会造成内存浪费

【扩展】当一个类“首次主动使用时”,虚拟机才会加载该类。以下几种情况会导致类的主动使用:

  • 创建类的实例
  • 访问某个类或接口的静态变量,或对该静态变量赋值
  • 调用类的静态方法
  • 反射(如Class.forName(“全限定类名”)
  • 初始化一个类的子类时,子类以及父类都会被使用
  • Java虚拟机启动时被标明为启动类的类(如包含main方法的类)
  • JDK7开始提供的动态语言支持

示例2【饿汉式写法-静态代码块的方式】:

//饿汉式(静态代码块)
class Singleton {
    //构造函数私有化,不能在外部new单例模式对象
    private Singleton(){}

    //静态变量存储单例类的唯一一个实例对象
    private static Singleton instance;

    //在静态代码块中创建单例对象
    static {
        instance = new Singleton();
    }

    //对外提供一个方法,获取该类的对象
    public static Singleton getInstance() {
        return instance;
    }
}

这种写法和第一种写法一样,都是在类装载的时候实例化对象,一次优缺点都一样。

懒汉式

示例3【懒汉式写法-线程不安全】:

class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if(null == instance) {
            instance = new Singleton();
        }
        return instance;
    }
}

优缺点

  • 起到了Lazy Loading的效果,但是只能在单线程下使用(即线程不安全
  • 如果在多线程下,一个线程进入了if(null == instance),但还没往下执行实例化单例对象,另一个线程此时判断到instance为null,也会进入if(null == instance),然后两个现象都会实例化单例对象,这便会产生多个实例
  • 不建议在实际开发中使用这种方式

示例4【懒汉式写法-线程安全-效率低】:

class Singleton {
    private static Singleton instance;

    private Singleton() {}

    //加上关键字synchronized,保证同时只有一个线程能执行这个方法体,解决线程安全问题
    public static synchronized Singleton getInstance() {
        if(null == instance) {
            instance = new Singleton();
        }
        return instance;
    }
}

优缺点:

  • 解决了懒汉式第一种写法的线程不安全问题
  • 效率低,每个线程去获取实例的时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码即可,其他线程向获取实例,直接return就行
  • 不建议在实际开发中使用这种方式
双重检查

示例5【双重检查写法-线程安全-效率高】:

class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

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

        return  instance;
    }
}

分析

  • 使用volatile关键字,当instance变量改变了,就会立即在内存中更新值
  • synchronized关键字放到一个代码块里
  • 即使有两个线程同时进入if(null == instance)代码块,当第一个线程进入同步代码块中实例化了一个单例对象之后,instance的值立即被更新,第二线程再进入同步代码块,判断到instance不为null,于是直接执行return

优缺点

  • Double-Check概念是多线程开发中经常使用到的,如上述代码中进行两次if(null == instance)判断,保证线程安全
  • synchronized关键字放到一个代码块里而不是getInstance()方法上,这样不必每个线程都会执行到同步代码块,当线程执行到第一个if(null == instance)时,检查到instance不为null,则直接返回,提高多线程效率
  • 总结:线程安全、延迟加载、效率高
  • 开发中推荐使用这用单例设计模式
静态内部类

示例6【静态内部类-线程安全-效率高】:

class Singleton{
    private Singleton() {}

    //静态内部类,静态属性存储单例对象
    private static class SingletonInstance{
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

分析

  • 当Singleton类装载的时候,静态内部类SingletonInstance类并不会被装载,只用在getInstance()方法中使用静态内部类变量INSTANCE的时候,该静态内部类才会被装载,保证延迟加载
  • 类只会被装载一次(首次主动使用时被装载),类的静态属性只会在第一次加载类的时候初始化,且类的装载过程是线程安全的,所有JVM就帮助我们保证了线程安全,在类初始化时,其他线程是无法进入的
  • 开发中推荐使用这用单例设计模式
枚举
enum Singleton {
	INSTANCE;
	public void sayOK() {
		System.out.println("ok");
	}
}

优缺点

  • 避免多线程同步问题
  • 防止方序列化重新创建新的对象
  • 《Effective Java》作者Josh Blosh提倡的方式

JDK源码中的单例模式

jdk1.8

java.lang.Runtime类使用了单例模式中的饿汉式写法:

package java.lang;

public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}

	//......
}

单例模式的使用场景

单例模式保证了系统中该类的对象只有唯一一个,节省了系统资源。对于一些需要频繁创建和销毁的对象、创建对象耗时过多或消费资源过多但又经常使用到的对象、工具类对象、频繁访问数据库或文件的对象(如数据源、session工厂)使用单例模式可以提高系统性能

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值