单例模式
单例模式(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();
}
}
}