饿汉式
package com.vapy.creator.Singleton;
/**
*
* @author vapy
*
* 饿汉式单例,线程安全
*
*/
public class Hungry {
private static final Hungry singleton = new Hungry();
private Hungry(){
}
public static Hungry getInstance(){
return singleton;
}
}
饿汉式是最常见的两种创建单例的方法之一,它是线程安全的,但却有一个小缺点:JVM在加载这个类时就创建了它的一个实例,不管系统是否真的能用到
懒汉式
package com.vapy.creator.Singleton;
/**
*
* @author vapy
*
* 懒汉式单例,线程安全
*
*/
public class Lazy {
private static Lazy singleton;
private Lazy(){
}
public static synchronized Lazy getInstance() {
if(null == singleton) {
singleton = new Lazy();
}
return singleton;
}
}
加上synchronized关键字的懒汉式,虽然由非线程安全变为了线程安全,也同样避免了饿汉式的缺点,但它也有一个缺点:多线程不能同时调用getInstance方法
双重检验锁
package com.vapy.creator.Singleton;
/**
*
* @author vapy
*
* 双重检验锁,线程安全
*
*/
public class DoubleChecked {
private volatile static DoubleChecked singleton;
private DoubleChecked() {
}
public static DoubleChecked getInstance() {
if (null == singleton) {
synchronized (DoubleChecked.class) {
if (null == singleton) {
singleton = new DoubleChecked();
}
}
}
return singleton;
}
}
双重检验锁只在首次调用getInstance时才加锁,之后每次都直接返回单例对象了,无需加锁,看起来完美的解决了懒汉式所带来的弊端。但是这里为什么要加上volatile关键字呢:
JVM执行singleton = new DoubleChecked();
并非原子操作,其实做了3件事,①为singleton
分配内存②调用构造方法,创建实例③让singleton
指向JVM给它分配的内存,但JVM会进行指令重排、优化的操作,这就导致了这3件事情的执行顺序不是固定的,如果JVM按照①③②的顺序进行操作的话,这会存在这样的问题:第一个线程执行完③后,singleton
已经不是null
了,此时第二个线程来了,它在外层检验时返回false
,它会直接返回一个singleton
实例,此时JVM尚未执行构造方法。问题出在了JVM的指令重排上,而volatile可以禁止JVM指令重排
此处本人文笔有限,可能描述的不是特别清楚,对volatile关键字不是很了解的同学,请点击此处
双重检验锁实现单例虽然没什么问题,但容易掉进JVM指令重排的陷阱中,下面写一种《Effective Java》推荐的写法
静态内部类
package com.vapy.creator.Singleton;
/**
*
* @author vapy
*
* 静态内部类,线程安全
*
*/
public class StaticNested {
private static class NestedSingleton {
private static final StaticNested INSTANCE = new StaticNested();
}
private StaticNested (){}
public static final StaticNested getInstance() {
return NestedSingleton.INSTANCE;
}
}
JVM在首次调用getInstance
时会加载内部类NestedSingleton
,创建一个的实例,而之后再调用getInstance
时,JVM不会重复加载NestedSingleton
,而且内部类是私有的,其它的类访问不了
反射面前,毫无障碍,本文的论点都是基于不用反射的基础上的,个人认为用了反射就不存在单例模式了。单例模式的目的是为了防止创建不必要的重要对象,而造成对系统资源的浪费,能做到这一点就足够了
本文代码可在github查看:点击此处