概念
单例就是指整个程序中该类的实例仅有一个,并且该类自己负责创建自己的实例并向整个系统提供这个实例。
单例模式用来保证一个类仅有一个实例并提供访问它的全局访问点。主要解决一个全局使用的类频繁地创建和销毁。用来控制实例数目,节省系统资源。
比如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工厂)使用单例模式可以提高系统性能
2620

被折叠的 条评论
为什么被折叠?



