java单例模式大全

单例模式:是一种常用的软件设计模式,在它的核心结构中值包含一个被称为单例的特殊类。一个类只有一个实例,即一个类只有一个对象实例。在spring框架中也是保证了所管理的对象都是单例模式;下面简单分析一下常用的单例模式以及各种单例模式的使用场景;

下文代码中的注解解释:

@NotThreadSafe :线程不安全
@Recommend:不推荐

单例模式1:懒汉模式

@NotThreadSafe
@Recommend
public class LazySingle {
    private LazySingle(){

    }
    /** 静态实例 **/
    private static LazySingle instance;

    /** 静态工厂方法 **/
    public static LazySingle getInstance(){
        if(instance == null) {
            instance = new LazySingle();
        }
        return instance;
    }
}

  

  懒汉模式:在单线程中可以很好的保证单例没有问题,但并发访问时候效果不是很好

单例模式2:饿汉模式

@ThreadSafe
@NotRecommend
public class HungSingle {
    private HungSingle(){

    }
    /** 静态实例 **/
    private static HungSingle instance = new HungSingle();

    /** 静态工厂方法 **/
    public static HungSingle getInstance(){
        return instance;
    }
}

  

  饿汉模式:通过静态实例实现单例模式,但是如果构造方法中有过多的处理会导致该方法加载很慢,导致性能问题

单例模式3:线程安全懒汉模式

@ThreadSafe
@NotRecommend
public class ThreadSafeLazySingle {
    private ThreadSafeLazySingle(){

    }
    /** 静态实例 **/
    private static ThreadSafeLazySingle instance;

    /** 静态工厂方法 **/
    public static synchronized ThreadSafeLazySingle getInstance(){
        if(instance == null) {
            instance = new ThreadSafeLazySingle();
        }
        return instance;
    }
}

  线程安全懒汉模式:通过synchronize关键字锁定静态工厂方法,但是再多线程中每次之允许一个线程获取实例,虽然保证了线程安全但是导致了性能开销

单例模式4:双重同步锁单例模式

@NotThreadSafe
@NotRecommend
public class DoubleCheckedLockingSingle {
    private DoubleCheckedLockingSingle(){ }

    /** 静态实例 **/
    private static DoubleCheckedLockingSingle instance;

    /** 静态工厂方法 **/
    public static DoubleCheckedLockingSingle getInstance(){
        //双重检测机制
        if(instance == null) {
            synchronized (DoubleCheckedLockingSingle.class) {
                if(instance == null) {
                    instance = new DoubleCheckedLockingSingle();
                }
            }
        }
        return instance;
    }
}

  双重同步锁单例模式:这个方法虽然使用了双重校验并且加锁但是任然是线程不安全的单例模式;

    jvm底层对象实例化的过程是:

      1、memory = allocate() 分配对象的内存空间
      2、cotrInstance() 初始化对象
      3、instance = momory 设置instance指向刚分配的内存
但是根据指令重排定义,可能存在以下执行顺序
      1、memory = allocate() 分配对象的内存空间
      3、instance = momory 设置instance指向刚分配的内存
      2、cotrInstance() 初始化对象 
   所以在多线程执行的时候可能存在:A线程执行,进入一层为空判断发现对象为空,加锁后进入对象实例化,执行到1,3步骤后,线程B线程调用此方法,此时由于线程A实例对象已经指向了分配的内存地址,所以一层判断是instance不等于null,直接返回instance,但是此时
返回的instance是尚未初始化的实例对象;

单例模式5:双重同步锁线程安全单例模式
@ThreadSafe
@Recommend
public class DoubleCheckedLockingThreadSafeSingleton {
    private DoubleCheckedLockingThreadSafeSingleton(){ }

    /** 静态实例  =>使用volatile限制指令重排 **/
    private volatile static DoubleCheckedLockingThreadSafeSingleton instance;

    /** 静态工厂方法 **/
    public static DoubleCheckedLockingThreadSafeSingleton getInstance(){
        //双重检测机制
        if(instance == null) {
            synchronized (DoubleCheckedLockingThreadSafeSingleton.class) {
                if(instance == null) {
                    instance = new DoubleCheckedLockingThreadSafeSingleton();
                }
            }
        }
        return instance;
    }
}

  双重同步锁线程安全同步模式:由三分析出双重同步锁不安全原因是应该指令重排序导致,想要防止指令重排序可以通过volatile关键字即可实现,volatile关键字这里不再赘述(volatile关键字使用场景:状态标识量和双重检测);

分隔线================================着重推荐下面两种线程安全的单例模式=============================================
单例模式6:通过内部类实现单例模式
@ThreadSafe
@Recommend
@Slf4j
public class InnerClassSingle {
    private InnerClassSingle() {
    }

    private static class InsideClass {
       public  final static InnerClassSingle instance = new InnerClassSingle();
    }

    public static InnerClassSingle getInstance(){
        return InsideClass.instance;
    }

    public static void main(String[] args) throws InterruptedException {
        int clientTotal = 10;
        ExecutorService executorService = Executors.newCachedThreadPool();
        CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal; i++) {
            executorService.execute(()-> {
                InnerClassSingle instance =InnerClassSingle.getInstance();
                log.info("实例化对象为:{}",instance.hashCode());
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
    }
}
这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,保证了线程安全同时也达到了lazy loading的效果,根据内部类装载规则,只有当调用了静态内部类的静态方法或者静态变量才会导致内部类的装载; 
单例模式7:通过枚举实现单例模式
@ThreadSafe
@Recommend
public class EnumThreadSafeSingleton {
    private EnumThreadSafeSingleton(){

    }
    public static EnumThreadSafeSingleton getInstance() {
        return Singleton.INSTANCE.getSingleton();
    }
    private enum Singleton {
        INSTANCE;

        private EnumThreadSafeSingleton singleton;

        //JVM保证这个方法只被绝对调用一次
        Singleton() {
            singleton = new EnumThreadSafeSingleton();
        }
        public EnumThreadSafeSingleton getSingleton() {
            return singleton;
        }
    }
}

  这种也是比较推荐的单例模式,通过jvm保证枚举内的方法只被调用一次失恋单例模式;

 

这里只是简单分析并发环境中的单例模式,详细代码例子可以从GitHub上下载:https://github.com/zlAdmin/didactic-enigma 项目中也包括大量的并发案例分享,并会持续更新


posted @ 2018-09-15 15:32  zlAdmin  阅读(937)  评论(0)    收藏  举报