单例模式
保证在同一个JVM中,同一个对象只存在一个实例。
类型:创建型模式
单例模式分为以下两种主流的形式:
- 懒汉模式
- 饿汗模式
两种形式的区别:
懒汉模式,在调用实例方法的时候才会实例化对象;饿汉模式在单例类被加载时候,就实例化一个对象交给自己的引用。饿汉模式不存在线程安全问题,懒汉模式需要考虑线程安全问题。
饿汉模式示例:
/**
* Create by zhaihongwei on 2018/3/12
*/
public class HungrySingleton {
// 私有化构造方法,防止外部实例化
private HungrySingleton() {
}
// 饿汉模式在类加载的时候,对象就已经被实例化
private static HungrySingleton hungrySingleton = new HungrySingleton();
// 获取实例的方法
public static HungrySingleton getInstance() {
return hungrySingleton;
}
}
懒汉模式示例:
最简单的懒汉模式示例:
/**
* Create by zhaihongwei on 2018/3/12
*/
public class LazySingleton {
// 私有化构造方法,防止外部实例化
private LazySingleton() {
}
// 懒汉模式,在类加载的时候并不实例化对象,而是在第一次调用获取实例化方法的时候实例化对象
private static LazySingleton lazySingleton = null;
// 获取对象实例的的方法
public static LazySingleton getInstance() {
// 判断实例是否为空,为空则创建实例
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
// 不为空,说明不是第一次调用获取实例的方法,直接返回实例。
return lazySingleton;
}
}
这种懒汉模式并不能解决高并发问题,是线程不安全的。以下是进行改造的:
// 获取对象实例的的方法加上synchronized 锁机制
public static synchronized LazySingleton getInstance() {
// 判断实例是否为空,为空则创建实例
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
// 不为空,说明不是第一次调用获取实例的方法,直接返回实例。
return lazySingleton;
}
但是这种synchronized关键字直接加在方法上,性能会有所下降,因为每次调用getInstance(),都需要将对象加锁,但是实际上我们只需要在第一次创建实例对象的时候需要加锁,所以有了以下改进:
// 获取对象实例的的方法
public static LazySingleton getInstance() {
// 判断实例是否为空,为空则创建实例,通过双重检查的方式,完成实例第一次创建的时候加锁。
if (lazySingleton == null) {
synchronized (LazySingleton.class) {
if(lazySingleton == null) {
lazySingleton = new LazySingleton();
}
}
}
// 不为空,说明不是第一次调用获取实例的方法,直接返回实例。
return lazySingleton;
}
还有一种通过静态内部类的形式完成实例的创建:
/**
* Create by zhaihongwei on 2018/3/12
*/
public class LazySingleton {
// 私有化构造方法,防止外部实例化
private LazySingleton() {
}
// 通过静态内部类的形式完成实例的创建
private static class LazySingletonFactory {
private static final LazySingleton lazySingleton = new LazySingleton();
}
// 获取实例的方法
public static LazySingleton getInstance() {
return LazySingletonFactory.lazySingleton;
}
}
使用这种静态内部类的形式完成实例的创建,原理是使用JVM类加载机制,JVM类加载机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的,所以当我们第一次调用getInstance()方法的时候,JVM能够保证实例只被创建一次,并且这种方式也只会在第一次调用的时候才会使用线程互斥机制,这种形式可以很好的解决线程安全问题,并且解决性能问题。具体使用哪种形式还需要根据不同的场景进行选取。