1、单例模式的应用场景
单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并且提供一个全局访问点。单例模式是创建型模式,在实际的开发过程中应用广泛,例如J2EE中的ServletContext、ServletContextConfig等,Spring框架中ApplicationContext、数据库连接池等都是单例形式。
2、饿汉式单例模式
饿汉式单例模式在类加载的时候就立即初始化,并且创建单例对象 。因此这种单例模式是天然的线程安全的,在线程还没有出现之前就已经实例化了。
优点:没有任何加锁,执行效率比较高;
缺点:类加载时就初始化,不管用不用都会占用内存,有一定的内存空间浪费。
Spring中ApplicationContext本身就是典型的饿汉式单例模式。
2.1、饿汉式写法一(利用类的初始化机制)
public class HungrySingleTon {
// Java类初始化顺序:先静态 后动态 --> 先属性 后方法 --> 从上至下
private static final HungrySingleTon hungrySingleTon = new HungrySingleTon();
// 私有化构造器
privite HungrySingleTon() {}
// 提供全局唯一访问点
public static HungrySingleTon getInstance() {
return hungrySingleTon;
}
}
2.2 、饿汉式写法二(利用静态代码块机制)
public class HungryStaticSingleTon {
private static final HungryStaticSingleTon hungryStaticSingleTon;
static {
hungryStaticSingleTon = new HungryStaticSingleTon();
}
private HungryStaticSingleTon() {}
public static HungryStaticSingleTon getInstance() {
return hungryStaticSingleTon;
}
}
饿汉式单例模式的两种写法都比较简单也很好理解,适用于单例对象较少的情况。
3、懒汉式单例模式
懒汉式单例模式的特点是:在外部类调用的时候内部类才会加载。
public class LazySimpleSingleTon {
private LazySimpleSingleTon() {}
// 静态块,公共内存区域
private static LazySimpleSingleTon lazy = null;
public LazySimpleSingleTon getInstance() {
if(lazy == null) {
lazy = new LazySimpleSingleTon();
}
return lazy;
}
}
上面的代码有一定的概率获得的对象出现不同的实例,意味着这种写法的单例模式不是线程安全的,为了使得懒汉式单例模式在多线程环境下安全,我们给getInstance()方法加上synchronized关键字,使这个方法变成线程同步方法:
public class LazySimpleSingleTon {
private LazySimpleSingelTon() {}
private static LazySimpleSingleTon lazy = null;
public synchronized static LazySimpleSingleTon getInstance() {
if(lazy == null) {
lazy = new LazySimpleSingleTon();
}
return lazy;
}
}
但是,用到synchronized关键字总归是要上锁,多线程的情况下锁竞争会有一定的性能损耗。我们从类初始化的角度来考虑,采用静态内部类的方式设计一个更好的方案,这种方案兼顾饿汉式内存浪费问题和懒汉式的性能问题:
public class LazyInnerClassSingleTon {
// 使用LazyInnerClassSingleTon的时候,默认会初始化内部类
// 如果没使用,则内部类是不加载的
private LazyInnerClassSingleTon() {}
// 每一个关键字都不是多余的,static是为了使单例的空间共享,保证这个方法不会被重写、覆盖
public static final LazyInnerClassSingleTon getInstance() {
return LazyHolder.LAZY
}
// 默认不加载
private static class LazyHolder {
private static final LazyInnerClassSingleTon LAZY = new LazyInnerClassSingleTon();
}
}
这种方式就是内部类保证在方法调用之前就初始化,巧妙的避免了线程安全的问题。