单例模式
意图
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
动机
适用性
需要确保一个类的实例必须全局唯一,并且可以全局访问。
结构
优点
- 对唯一实例的受控访问。
- 缩小名空间,可以避免那些存储唯一实例的全局变量污染名空间。
实现
懒汉式(线程不安全)
public class Singleton01 {
/**
* 默认不再初始化
*/
private static Singleton01 singleton = null;
/**
* 构造器私有
*/
private Singleton01(){
}
/**
* 暴露出实例方法
*/
public static Singleton01 getInstance(){
if(singleton == null ){
singleton = new Singleton01();
}
return singleton;
}
}
饿汉式(线程安全)
public class Singleton {
/**
* 自己构造自己的实例,设置private,外界无法访问
*/
private static final Singleton singleton = new Singleton();
/**
* 构造器私有,外界无法通过new关键字创建对象
*/
private Singleton(){
}
/**
* 暴露得到实例的方法
*/
public static Singleton getInstance(){
return singleton;
}
}
懒汉式(线程安全)
public class Singleton01 {
/**
* 默认不再初始化
*/
private static Singleton01 singleton = null;
/**
* 构造器私有
*/
private Singleton01(){
}
/**
* 暴露出实例方法
* synchronized对性能影响特别大,每次访问都会加锁
*/
public static synchronized Singleton01 getInstance(){
if(singleton == null ){
singleton = new Singleton01();
}
return singleton;
}
}
懒汉式–双重校验锁(线程安全)
public class Singleton02 {
private volatile static Singleton02 singleton = null;
private Singleton02(){ }
/**
* 暴露出实例方法
* synchronized加上可以保证线程安全,但是会大幅度降低系统性能,因为每次请求都加锁。
*/
public static Singleton02 getInstance(){
//加锁前检测
if(singleton == null ){
synchronized (Singleton02.class){
//加锁后检测
if(singleton == null){
singleton = new Singleton02();
}
}
}
return singleton;
}
}
单层锁为什么不行?
if (singleton == null) {
synchronized (Singleton02.class) {
singleton = new Singleton02();
}
}
假如两个线程同时执行了if (singleton == null)语句,并且都通过了if检测,即使if语句里面有synchronized锁, singleton = new Singleton02()依然会被两个线程分别执行一次,因为假如第一个先进入同步代码块执行,第二个线程等待,当第一个执行结束,第二个依然可以获得锁去执行。
静态内部类实现
利用静态内部类仅在类初始化的时候加载一次的性质。该实现也属于懒汉式,因为刚Singleton初始化的时候private static final Singleton INSTANCE = new Singleton()并不会执行,只有调用getUniqueInstance方法的时候才会执行。
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举实现(重要)
这是单例模式的最佳实践,它实现简单,并且在面对复杂的序列化或者反射攻击的时候,能够防止实例化多次。
public enum Singleton{
/**
* 唯一实例
*/
uniqueInstance;
private Singleton(){
System.out.println("here");
}
public void doSomething(){
System.out.println("hello");
}
public static void main(String[] args) {
//在此执行了私有方法,也即执行new Singleton()的地方
Singleton uniqueInstance = Singleton.uniqueInstance;
// Singleton uniqueInstance2 = Singleton.uniqueInstance;
// System.out.println(uniqueInstance.equals(uniqueInstance2)); true
//调用一些方法
uniqueInstance.doSomething();
}
}