前言
在开发过过程中会使用到单例模式,常见的单例模式有以下几种:
- 饿汉模式
- 饿汉模式静态代码块
- 懒汉模式
- 懒汉同步方法模式
- 懒汉方法内同步方式
- 懒汉双重检查模式
- 内部类模式
- 枚举模式
1. 饿汉模式
public class HungryMode {
private HungryMode(){}
private static HungryMode lazyMode = new HungryMode();
public static HungryMode getLazyMode() {
return lazyMode;
}
public static void main(String[] args) {
HungryMode lazyMode = getLazyMode();
System.out.println(lazyMode);
}
}
1.1 优点
这种类型的饿汉模式是线程安全的,通过jvm类加载机制确保了线程安全
1.2 缺点
饿汉模式采用了静态变量,类装载的时候就创建了对象,没有实现对象的懒加载可能会造成对内存的浪费
2.饿汉模式静态代码块
public class HungryStaticMode {
private HungryStaticMode(){}
private static HungryStaticMode instance;
static {
instance = new HungryStaticMode();
}
public static HungryStaticMode getInstance() {
return instance;
}
}
2.1 优点
这种类型的饿汉模式是线程安全的,通过类装载确保了线程安全
2.2 缺点
类装载的时候就会加载静态代码块时,就创建了对象,无法实现懒加载,可能会造成对内存的浪费
3 懒汉模式
懒汉模式是在静态变量创建对象默认没有进行赋值,然后获取实例进行非空判断。
public class LazysMode {
private LazysMode(){}
private static LazysMode hungryMode;
public static LazysMode getHungryMode() {
if (hungryMode == null){
hungryMode = new LazysMode();
}
return hungryMode;
}
public static void main(String[] args) {
/* for (int i =0; i<100;i++){
HungryMode hungryMode = HungryMode.getHungryMode();
System.out.println(hungryMode);
}*/
for (int i = 0; i< 1000; i ++){
new Thread(new Runnable() {
@Override
public void run() {
LazysMode hungryMode = LazysMode.getHungryMode();
System.out.println(hungryMode);
}
}).start();
}
}
}
3.1 优点
懒汉模式在调用实例的时候在创建对象,实现了实例的懒加载。
3.2 缺点
在多线程环境下,线程不安全。
4 懒汉同步方法模式
懒汉同步方法模式就是在懒汉模式的基础上在获取实例方法中加上synchronized关键字,获取实例方法阻塞,从而实现线程安全。
public class LazySyconizedMode {
private LazySyconizedMode(){}
private static LazySyconizedMode lazySyconizedMode;
/**
* 加入synchronized 悲观锁
* 同步,线程便安全
* 同步会进行阻塞
* @return
*/
public static synchronized LazySyconizedMode getLazySyconizedMode() {
if (lazySyconizedMode == null)
lazySyconizedMode = new LazySyconizedMode();
return lazySyconizedMode;
}
}
4.1 优点
加入synchronized, 通过synchronized关键字阻塞方法,实现方法的同步,实现了线程安全
4.2 缺点
使用synchronized关键字锁住整个方法,方法调用效率变低。
5 懒汉方法内同步方式
懒汉方法内同步方式是在懒汉模式的基础上在创建对象实例时,执行同步方法添加 synchronized 关键字
public class LazySynchronizedMode {
private LazySynchronizedMode(){}
private static LazySynchronizedMode lazySynchronizedMode;
public static synchronized LazySynchronizedMode getLazySyconizedMode() {
if (lazySynchronizedMode == null){
synchronized (LazySyconizedMode.class){
lazySynchronizedMode = new LazySynchronizedMode();
}
}
return lazySynchronizedMode;
}
}
5.1 缺点
非线程安全, 可能再多线程情况下产生多个实例 不能使用
6 懒汉双重检查模式
使用volatile 关键字实现了线程变量的共享
- 1、进行了两次 if null 的判断
- 2、双重检查避免了多次同步,
- 3、使用延迟加载提供了效率
- 4、这种模式实现了线程安全
public class LazyDoubleCheckMode {
private static volatile LazyDoubleCheckMode lazyDoubleCheckMode;
private LazyDoubleCheckMode(){}
public static LazyDoubleCheckMode getLazyDoubleCheckMode() {
if (Objects.isNull(lazyDoubleCheckMode)){
synchronized (LazyDoubleCheckMode.class){
if (Objects.isNull(lazyDoubleCheckMode)){
lazyDoubleCheckMode = new LazyDoubleCheckMode();
}
}
}
return lazyDoubleCheckMode;
}
}
6.1 优点
懒汉模式 双重检查,线程安全,实现了懒加载,方法比较优秀,比较推荐使用
7 内部类模式
通过内部类的模式创建对象实例和返回实例
public class InnerClassMode {
private static class SingletonHolder{
private static final InnerClassMode INSTANCE = new InnerClassMode();
}
private InnerClassMode(){}
public static InnerClassMode getInstance() {
return SingletonHolder.INSTANCE;
}
public static void main(String[] args) {
System.out.println(getInstance());
}
}
7.1 优点
类被装载的时候内部类并不会立即被装载,所以支持懒加载的,线程安全+懒加载 推荐使用
8 枚举模式
使用枚举模式实现单利模式
/**
* 单例模式
*/
public enum SingletonEnum {
INSTANCE;
public static SingletonEnum testWhatever(){
return SingletonEnum.INSTANCE;
}
public static void main(String[] args) {
System.out.println(testWhatever());
}
}
总结
在平常的开发过程中,推荐使用双重检查模式和内部类模式实现单例模式,这两种模式同时实现了线程安全和懒加载。