前言
单例模式:单例模式是比较常见的一种设计模式,其优点也是比较明显
1.它能避免对象重复创建,节约空间并提升效率
2.避免有偶遇操作不同实例导致逻辑错误
单例模式的构建
1.1饿汉式
三步完成:
- new一个静态对象
- 私有构造器
- 提供个共有化的对象获取方法
public class HungrySingleton {
private static HungrySingleton singleton = new HungrySingleton();
//构造器私有化
private HungrySingleton(){}
public static HungrySingleton getInstance() {
return singleton;
}
}
结论
饥饿胡汉三疯狂的想找对象 ,以至于使用的招数下流简单粗暴
所以找到的对象也不是正经安全的对象 = 线程不安全的单例模式
1.2懒汉式
构建也是与饿汉式比较的相似
public class LazySingleton {
private static LazySingleton singleton;
//私有化构造器
private LazySingleton(){}
//synchronized 加上同步才能实现单例
public static synchronized LazySingleton getInstance() {
if (null == singleton){
singleton = new LazySingleton();
}
return singleton;
}
}
当然也有另一种写法:
这种写法锁颗粒度更细,锁之外可有更多的扩展。
public class LazySingleton {
private static LazySingleton singleton;
//私有化构造器
private LazySingleton(){}
public static LazySingleton getInstance() {
synchronized(LazySingleton.class){
if (null == singleton){
singleton = new LazySingleton();
}
}
return singleton;
}
}
以上写法是乍一看没有毛病,但是多线程情况下同时进入getInstance()方法,每次都要执行synchronized同步化方法,这样会严重影响性能,所以更高级的做法就是在同步前,再加上一层检查。
这种方式又叫做:双重校验锁单例模式
public class LazySingleton {
private static LazySingleton singleton;
//私有化构造器
private LazySingleton(){}
public static LazySingleton getInstance() {
if (null == singleton){
synchronized(LazySingleton.class){
if (null == singleton){
singleton = new LazySingleton();
}
}
}
return singleton;
}
}
结论
懒汉懒汉虽然很懒,又渴望对象
懒汉找对象肯定想找个比较好的对象,来弥补自己的懒散,然而自己又比较懒,所以找得比较慢
故:懒汉式效率低而线程安全
1.3静态内部类
静态内部类的方式在实际开发环境中还是比较少用的,原因就是它用懒加载的方式实现的。
那么静态内部类是怎么去实现懒加载的呢?
Java类加载过程中包含:加载、验证、准备、解析、初始化。
初始化阶段就是执行clinit方法(clinit = class + initialize),包括为类的静态变量赋初始值和执行静态代码块中的内容。
但是不会立即执行加载内部类,而是在调用它时才会加载。
所以当此StaticInnerClassSingleton 类加载时,Singleton 并不会被加载,所以不向饿汉式那样占用内存。
其次,Java虚拟机规定,当访问一个类的静态字段时,如果此类未被初始化,则立即初始化此类。
所以但StaticInnerClassSingleton 调用getInstance 方法时,由于使用到了Singleton 的SINGLETON 变量,这时候才会去初始化内部类。
这就实现了懒加载。
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton(){}
//实质上就式在静态类中去建立一个对象
private static class Singleton {
private static final StaticInnerClassSingleton SINGLETON = new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance(){
return Singleton.SINGLETON;
}
}
1.4枚举
枚举时jdk1.5之后才出现的,估计很多小伙伴用的都比较少吧
public enum EnumSingleton {
SINGLETON;
public void method(){
}
}
枚举类型使用起来也是简单,直接EnumSingleton.SINGLETON 调用即可。
总结
笔者使用的比较多的是枚举类型,使用起来简单方便。不过还是要根据自己的环境进行取舍。一般建议是对于那些构建不复杂的,加载完立即使用的,建议使用饿汉式。对于构建耗时较长的使用较少的,建议使用懒汉式。因为程序要讲构建和使用分离,达到更好的解耦,所以这里一般很少用到静态内部类的方式。