单例模式的几种实现

单例模式

单例模式(Singleton Pattern)是一种简单的对象创建型模式。该模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。

所以要实现单例模式,要做到以下几点:

  • 将构造方法私有化,杜绝使用构造器创建实例。
  • 需要自身创建唯一的一个实例,并提供一个全局访问入口。

一、饿汉式

饿汉式单例模式,利用类加载机制来避免了多线程的同步问题,所以是线程安全的,可以保证多个线程下的唯一实例。

  • 优点:未加锁,执行效率高。
  • 缺点:类加载的时候单例对象就产生了,造成内存浪费。
1.1 采用静态常量final写法
//写法1:采用常量final写法
public class HungerSingleton  {
	
	//私有实例变量,静态变量会在类加载的时候初始化且仅一次,是线程安全的
	private static final HungerSingleton INSTANCE = new HungerSingleton ();
	
	//将构造方法私有化,不允许外部new
	private HungerSingleton () {}
	
	//唯一公开获取实例的方法(静态工厂方法)
	public static HungerSingleton getInstance() {
		return INSTANCE;
	}

}

1.2 采用静态代码块的写法
//写法2:采用静态代码块的写法
public class HungerSingleton  {
	
	//定义私有静态实例变量
	private static HungerSingleton instance;
	
	//使用静态代码块来实例化
	static {
		instance = new HungerSingleton();
	}
	
	//将构造方法私有化,不允许外部new
	private HungerSingleton () {}
	
	//唯一公开获取实例的方法(静态工厂方法)
	public static HungerSingleton getInstance() {
		return instance;
	}

}

二、懒汉式

在第一次调用才初始化,避免了内存浪费(优点)。

2.1 普通写法

使用synchronized关键字进行加锁,保证线程安全性。但缺点也随之而来:对获取实例方法加锁,大大降低了并发效率。

由于加了锁,对性能影响较大,不推荐使用。

//无加锁,线程不安全
public class LazySingleton {
	
	//定义私有静态实例变量
	private static LazySingleton instance;
	
	//将构造方法私有化,不允许外部new
	private LazySingleton () {}
	
	//唯一公开获取实例的方法(静态工厂方法)
	public static LazySingleton getInstance() {
		if (instance == null) {
			instance = new LazySingleton();
		}
		return instance;
	}

}


//加锁,线程安全,但是效率低
public class LazySingleton {
	
	//定义私有静态实例变量
	private static LazySingleton instance;
	
	//将构造方法私有化,不允许外部new	
	private LazySingleton () {}
	
	//唯一公开获取实例的方法(静态工厂方法)
	public static synchronized LazySingleton getInstance() {
		if (instance == null) {
			instance = new LazySingleton();
		}
		return instance;
	}

}

//线程不安全
public class LazySingleton {
	
	//定义私有静态实例变量
	private static LazySingleton instance;
	
	//将构造方法私有化,不允许外部new	
	private LazySingleton () {}
	
	//唯一公开获取实例的方法(静态工厂方法)
	public static LazySingleton getInstance() {
		if (instance == null) {
			synchronized (LazySingleton.class) {
				instance = new LazySingleton();
			}
		}
		return instance;
	}

}
2.2 双重校验锁

利用了volatile修饰符的线程可见性(被一个线程修改后,其他线程立即可见),即保证了懒加载,又保证了高性能,所以推荐使用。

//线程安全,使用双重判断机制
//同时保证效率,推荐使用	
public class LazySingleton {
	
	//定义私有静态实例变量
	private volatile static LazySingleton instance;
	
	//将构造方法私有化,不允许外部new	
	private LazySingleton () {}
	
	//唯一公开获取实例的方法(静态工厂方法)
	public static LazySingleton getInstance() {
		if (instance == null) {
			synchronized (LazySingleton.class) {
				if (instance == null) {
					instance = new LazySingleton();
				}
			}
		}
		return instance;
	}

}

2.3 静态内部类

该模式利用了静态内部类延迟初始化的特性,来达到与双重校验锁方式一样的功能。由于需要借助辅助类,并不常用。

//线程安全
public class LazySingleton {
	
	//将构造方法私有化,不允许外部new	
	private LazySingleton () {}
	
    //静态内部类
	private static class LazyHolder {
		 private static final LazySingleton INSTANCE = new LazySingleton();
	}
	
	//唯一公开获取实例的方法(静态工厂方法)
	public static LazySingleton getInstance() {
		return LazyHolder.INSTANCE;
	}

}
2.4 枚举类

在《effective java中》说道,最佳的单例实现模式就是枚举模式。该方式利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题。这不仅能避免线程同步问题,还防止反射或反序列化重新创建新的对象。除此之外,写法还特别简单。(jdk1.5以上才适用)

枚举单例可以有效防御两种破坏单例(即让单例产生多个实例)的行为:反射攻击与序列化攻击。
反射可以侵入单例类的私有构造方法并强制执行,使之产生多个不同的实例,这样单例就被破坏了。

It is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks.

//线程安全
public enum SingletonEnum {

    INSTANCE;

    public void doSomething() {
        System.out.println("doSomething");
    }
}

//测试
public class Test {
	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			SingletonEnum.INSTANCE.doSomething();
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值