Java设计模式-单例模式
1. 单例模式
单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点!
单例模式应该是23种设计模式中最简单的一种模式了。它有以下几个要素:
1. 私有的构造方法
2. 指向自己实例的私有静态引用
3. 以自己实例为返回值的静态的公有的方法
单例模式5种写法-- 饿汉式单例,懒汉式单例,双层校验锁,静态内部类,枚举
饿汉式单例在单例类被加载时候,就实例化一个对象交给自己的引用;
懒汉式在调用取得实例方法的时候才会实例化对象。
2. 饿汉式单例
饿汉法就是在第一次引用该类的时候就创建对象实例,而不管实际是否需要创建,好处是编写简单,但是无法做到延迟创建对象。
/**
* 饿汉式单例
*/
public class Singleton {
// 将自身实例化对象设置为一个属性,并用 static、final 修饰
private static final Singleton singleton = new Singleton();
// 构造方法私有化
private Singleton (){}
// 静态方法返回该实例
public static Singleton getInstance(){
return singleton;
}
}
3. 懒汉式单例
懒汉式单例模式,线程不安全,由私有构造器和一个公有静态工厂方法构成,在工厂方法中对singleton进行null判断,
如果是null就new一个出来,最后返回singleton对象。这种方法可以实现延时加载(lazy landing),但是有一个致命弱点:线程不安全。
如果有两条线程同时调用getSingleton()方法,就有很大可能导致重复创建对象。
/**
* 懒汉式单例
*/
public class Singleton {
// 将自身实例化对象设置为一个属性,并用 static 修饰
private static Singleton singleton;
// 构造方法私有化
private Singleton() {}
// 静态方法返回该实例
public static synchronized Singleton getSingleton() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
4. 双层校验锁
双重非空判断,new对象前加一次锁。
volatile关键字,由于volatile关键字屏蔽了虚拟机中一些必要的代码优化,所以运行效率并不是很高,因此建议没有特
别的需要不要使用,而volatile关键字可以防止指令重排。
/**
* 双层校验锁
*/
public class Singleton {
// 将自身实例化对象设置为一个属性,并用 volatile 修饰
private volatile static Singleton singleton;
// 构造方法私有化
private Singleton() {}
// 静态方法返回该实例
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
5. 静态内部类
由于静态内部类跟外部类是平级的,所以外部类加载的时候不会影响内部类,因此实现了lazy loading,同时也是利用
静态变量的方式,使得INSTANCE只会在SingletonHolder加载的时候初始化一次,从而保证不会有多线程初始化的情
况,因此也是线程安全的。
/**
* 静态内部类
*/
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
6. 枚举
这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创
建新的对象,是线程安全的,但是很少见使用。
/**
* 枚举
*/
public enum Singleton {
INSTANCE;
public void whateverMethod() {}
}
7. 饿汉式和懒汉式区别
1. 从名字上来说,饿汉和懒汉,饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,而懒汉模式,只有当调用getInstance的时
候,才会去初始化这个单例。
2. 饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,懒汉式本身是非线程安全的。
3. 饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已
经初始化完成,而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,
之后就和饿汉式一样了。
4. 在Java中,饿汉式单例要优于懒汉式单例。
8. 单例模式的优点
1. 在内存中只有一个对象,节省内存空间。
2. 避免频繁的创建销毁对象,可以提高性能。
3. 避免对共享资源的多重占用。
4. 可以全局访问。
9. 适用场景
由于单例模式的以上优点,所以是编程中用的比较多的一种设计模式。以下是一些适合使用单例模式的场景:
1. 需要频繁实例化然后销毁的对象。
2. 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
3. 有状态的工具类对象。
4. 频繁访问数据库或文件的对象。
5. 以及其他我没用过的所有要求只有一个对象的场景。
10. 单例模式注意事项
1. 只能使用单例类提供的方法得到单例对象,不要使用反射,否则将会实例化一个新对象。
2. 不要做断开单例类对象与类中静态引用的危险操作。
3. 多线程使用单例使用共享资源时,注意线程安全问题。