一、单例模式的定义
单例模式 :确保某个类只有一个实例,且自行实例化并向整个系统提供这个实例。
二、单例模式的优缺点及应用场景
优点:单例模式在内存中只有一个实例,大大减少了内存开支,特别是一个对象需要频繁地创建、销毁时,单例模式的优势就非常明显。单例模式可以在系统设置全局的访问点,优化和共享资源访问。
缺点:单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。
使用场景:在一个系统中,要求一个类有且仅有一个对象,或者创建一个对象需要消耗的资源过多,都可以使用单例模式;如spring中的bean对象默认是单例模式。
三、单例模式示例代码
3.1、饿汉式
//饿汉式单例,在类初始化时创建,线程安全
public class Singleton {
//构造方法私有化,防止外界创建
private Singleton() {}
//创建对象
private static final Singleton single=new Singleton();
//获取实例对象
public static Singleton getInstance() {
return single;
}
}
3.2、懒汉式
//懒汉式单例.在第一次调用的时候创建,线程不安全
public class Singleton {
//构造方法私有化,防止外界创建
private Singleton() {}
private static final Singleton single=null;//单例对象
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
懒汉式单例模式在低并发的情况下尚不会出现问题,若系统压力增大,并发量增加时则可能在内存中出现多个实例,破坏了最初的预期。
如一个线程A执行到singleton= new Singleton(),但还没有获得对象(对象初始化是需要时间的),第二个线程B也在执行,执行到(singleton == null)判断,那么线程B获得判断条件也是为真,于是继续运行下去,线程A获得了一个对象,线程B也获得了一个对象,在内存中就出现两个对象!
要实现线程安全,有以下三种方式:
1、在getInstance方法上加锁同步
public class Singleton {
//构造方法私有化
private Singleton() {}
private static final Singleton single=null;//单例对象
public static synchronized Singleton getInstance() {//同步锁
if (single == null) {
single = new Singleton();
}
return single;
}
}
此方法虽保证了线程安全,但每次调用都要获取锁,对性能有很大影响。
2、双重检查
public class Singleton {
//构造方法私有化
private Singleton() {}
//volatile保证可见性和禁止指令重排序
private volatile static Singleton single=null;
public static Singleton getInstance() {
if (single == null) { // 第一重检查锁定
synchronized (Singleton.class) {//同步锁
if (single == null) { // 第二重检查锁定
single = new Singleton();
}
}
}
return single;
}
}
此方法比第一种好一些,只有第一次访问(还未创建对象)才需要获取锁。
3、静态内部类
public class Singleton {
private static class LazyHolder {
//被调用时加载
private static final Singleton single= new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return LazyHolder.single;
}
使用内部类的好处是,静态内部类不会在单例加载时就加载,而是在调用getInstance()方法时才进行加载,达到了类似懒汉模式的效果,而这种方法又是线程安全的,同时又避免了同步锁带来的性能影响。