java设计模式之单例模式singleton
什么是单例模式?
单例模式是:一个类在任何情况下都绝对只有一个实例。
为什么要使用单例模式?
需要控制实例数量,防止实例重复创建、销毁,造成资源浪费。
如何实现单例模式?
- 需要私有化构造函数
- 需要提供一个全局访问入口
- 需要线程安全
- 需要防止反序列化
单例模式的实现方式,大致有8中写法:
1.饿汉式写法 (推荐)
- 缺点:
- 无论是否用到,类加载时就会初始化
- 不能防止反序列化
- 优点:
- 代码实现简单
- 线程安全 (jvm保证了线程安全)
两种写法
public class Singleton{
/* 静态成员变量,在类加载时实例化一个对象 */
private static final Singleton SINGLETON = new Singleton();
/* 私有构造 */
private Singleton() {};
/*全局访问入口 */
public static Singleton getInstance() {
return SINGLETON;
}
}
public class Singleton {
private static final Singleton SINGLETON;
/* 静态代码块,类加载时实例化一个对象 */
static {
SINGLETON = new Singleton();
}
/* 私有构造 */
private Singleton() {};
/*全局访问入口 */
public static Singleton getInstance() {
return SINGLETON;
}
}
2.懒汉式写法
- 缺点:
- 不能防止反序列化
- 优点:
- 按需使用 (只有在用到的时候才会实例化对象)
写法一:线程不安全
public class Singleton{
private static Singleton SINGLETON;
private Singleton() {
}
public static Singleton getInstance() {
if (SINGLETON == null) {
SINGLETON = new Singleton();
}
return SINGLETON;
}
}
写法二:线程安全,但效率降低
public class Singleton{
private static Singleton SINGLETON;
private Singleton() {
}
/* 加了synchronized 保证了线程安全,但效率降低。*/
public static synchronized Singleton getInstance() {
if (SINGLETON == null) {
SINGLETON = new Singleton();
}
return SINGLETON;
}
}
写法三:双重检查,线程安全,但不够完美
public class Singleton {
private static volatile Singleton SINGLETON; //JIT
private Singleton() {
}
public static Singleton getInstance() {
if (SINGLETON == null) {
// 双重检查
synchronized (Singleton.class) {
if(SINGLETON == null) {
SINGLETON = new Singleton();
}
}
}
return SINGLETON;
}
}
写法四:静态内部类写法,线程安全(由 jvm 保证线程安全),相对完美
public class Singleton {
private Singleton() {
}
/* 类加载时不会记载内部类 */
private static class SingletonHolder {
private final static Singleton SINGLETON = new Singleton();
}
/* 调用getInstance时,加载其内部类,同时实例化内部类的成员变量,实现了懒加载 */
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
3.枚举写法
比较完美的写法
- 优点:
- 防止反序列化
- 线程安全
public enum Singleton {
SINGLETON;
/* 测试一下 */
public static void main(String[] args) {
for(int i=0; i<100; i++) {
new Thread(()->{
System.out.println(Singleton.SINGLETON.hashCode());
}).start();
}
}
}