1:饿汉式
public class Hungry {
private static final Hungry hungry = new Hungry();
private Hungry(){}
public static Hungry getInstance(){
return hungry;
}
}
特点:类加载的时候就创建的对象,简单,无线程安全问题
2:懒汉式
public class Lazy {
private Lazy(){}
private static Lazy lazy;
public static synchronized Lazy getInstance(){
if (lazy == null){
lazy = new Lazy();
}
return lazy;
}
}
特点:延迟加载,在需要使用的时候加载,方法加锁并发很低
3:双重检查锁
public class DoubleCheck {
private DoubleCheck(){}
private static volatile DoubleCheck doubleCheck;
public static DoubleCheck getInstance(){
// 第一重的目的是对象已经初始化了,不用再初始化,也可以换成!=null 直接返回
// if (doubleCheck != null){
// return doubleCheck;
// }
if (doubleCheck == null){
// 第二个if判断的目的是为了防止生成多个对象,多个线程可能同时进入这一步
synchronized (DoubleCheck.class){
if (doubleCheck == null){
doubleCheck = new DoubleCheck();
}
}
}
return doubleCheck;
}
}
特点:并发高,并发安全,延迟加载,volatile的作用,new操作不是原子性的,可能会重排序导致其他线程获取到一个null对象,有人说new 操作已经是一个原子性的操作。(还有一种可能,同步结束后对象直接写入内存,但是其他线程如果没从内存里面获取不也是为null?)synchronized的内存语义,写对读可见,上面括号的情况应该不存在,其他线程获取锁后读取到的对象是从内存中获取的。
4:静态内部类
public class StaticInnerClass {
private StaticInnerClass(){}
private static class ClassHolder{
public static StaticInnerClass staticInnerClass = new StaticInnerClass();
}
public static StaticInnerClass getInstance(){
return ClassHolder.staticInnerClass;
}
}
特点:懒汉式的加载,当一个类的静态属性被调用时会触发该类的加载,ClassHolder.staticInnerClass触发了ClassHolder的加载,由jvm自己保证线程安全。
5:枚举
public enum EnumSingle {
INSTANCE;
}
特点:好简单
6:分布式下的单例模式
分布式锁+序列化
7:单例模式特点
单例模式简单,全局唯一一个对象,节省内存和资源。单例模式不符合面向对象编程,无法按需注入依赖,扩展差,在项目中可以通过配置文件或者定时任务更新单例对象信息。(还可以通过getInstance传参数修改属性?不符合单例的特点。单例的特点上层直接获取对象使用。)
8:由单例模式可能引发的面试
项目中使用单例模式的情况,双重校验锁的两个if判断作用,volatile关键字的作用,volatile和synchronized有什么联系或者区别?->多线程相关
网友评论