PupilYu 2019年03月28日
Java中稍微细分的话有七种单例模式,下面由简单到复杂,由缺陷到全面逐一介绍。
1.饿汉式
个人理解饿汉式的含义是饥饿难耐,上来就要“吃到东西”。代码如下:
/**
* 饿汉式单例模式
* 简单程度:★★★★★
* 全面程度:★☆
* 优点:代码简洁
* 缺点:类加载时就初始化了对象
*/
public class Singleton1 {
//私有化静态对象
private static Singleton1 singleton1 = new Singleton1();
//私有化构造方法
private Singleton1() {
}
//获取实例方法
public static Singleton1 getInstance() {
return singleton1;
}
}
2.懒汉式
懒汉式有懒加载的意思,使用时才加载。代码如下:
/**
* 懒汉式单例模式
* 简单程度:★★★★
* 全面程度:★★
* 优点:相较于饿汉式延迟了加载时机,第一次使用时才加载对象
* 缺点:非线程安全
*/
public class Singleton2 {
private static Singleton2 singleton2;
private Singleton2() {
}
public static Singleton2 getInstance() {
if (singleton2 == null) {
singleton2 = new Singleton2();
}
return singleton2;
}
}
3.同步锁式
同步锁式解决了线程安全问题,但又出现了效率问题。代码如下:
/**
* 同步锁式单例模式
* 简单程度:★★★☆
* 全面程度:★★☆
* 优点:相较于懒汉式保证了线程安全
* 缺点:这种加锁方式导致效率下降
*/
public class Singleton3 {
private static Singleton3 singleton3;
private Singleton3() {
}
//获取实例的方法改成用 synchronized 修饰的同步方法
public static synchronized Singleton3 getInstance() {
if (singleton3 == null) {
singleton3 = new Singleton3();
}
return singleton3;
}
}
4.双重检测式
双重检测式为了提高效率,在代码简洁度上做了让步。代码如下:
/**
* 双重检测式单例模式
* 简单程度:★★★
* 全面程度:★★★
* 优点:相较于同步锁式仅在第一次获取时会加锁,效率有所提高
* 缺点:双重判断,写法繁琐
*/
public class Singleton4 {
//用 volatile 修饰,保证实例的状态对所有线程可见
private static volatile Singleton4 singleton4;
private Singleton4() {
}
public static Singleton4 getInstance() {
if (singleton4 == null) {
synchronized (Singleton4.class) {
//如果同步代码块内部不再次判断非空,仍然会产生线程安全问题
if (singleton4 == null) {
singleton4 = new Singleton4();
}
}
}
return singleton4;
}
}
5.静态内部类式
代码如下,当任何一个线程第一次调用 getInstance() 时,都会使 Singleton5Holder 加载,也就是被调用时才进行初始化,Java机制保证了初始化静态数据时的线程安全性,所以不需要任何的同步措施。
/**
* 静态内部类式单例模式
* 简单程度:★★★★
* 全面程度:★★★★
* 优点:被调用时才初始化,同时保证线程安全性
* 缺点:无
*/
public class Singleton5 {
private Singleton5() {
}
private static class Singleton5Holder {
private final static Singleton5 singleton5 = new Singleton5();
}
public static Singleton5 getInstance() {
return Singleton5Holder.singleton5;
}
}
6.枚举实现单例
/**
* 枚举式单例模式
* 简单程度:★★★★☆
* 全面程度:★★★★★
* 优点:保证线程安全,保证序列和反序列化情况下的单例
* 缺点:无
*/
public enum Singleton6 {
INSTANCE;
//在枚举内部实现 enumSingleton 对象的单例
private EnumSingleton enumSingleton;
private Singleton6() {
enumSingleton = new EnumSingleton();
}
public EnumSingleton getInstance() {
return enumSingleton;
}
}
public class EnumSingleton {
//内部实现...
}
7.容器方式实现单例
用 HashMap 不能存放相同对象的特性来达到单例的效果,这里就不详细介绍了。
另外饿汉式还有一种变种,就是在静态代码块中初始化对象,其实无关紧要,也不详细说了。
其实对某些单例模式用到的底层机制并不了解,只知道这么用能保证单例,只是先在此记录一下。