Singleton
分为懒汉式和饿汉式和登记式,适用于一个类只有一个实例的情况(保证一个类仅有一个实例,并提供一个该实例的全局访问点)
懒汉式:在第一次调用方法的时候会做一个判断,如果实例不存在,则创建一个,如果实例存在,则直接返回。延迟加载,比如配
配置文件,被用到的时候才会加载
饿汉式:一开始就加载了
① 懒汉模式
/**
*
* 懒汉模式:第一次被引用时候才被初始化
*/
public class Singleton {
private final static Object syncLock = new Object();
// 设立静态变量
private static volatile Singleton instance = null;
//让构造函数为 private,这样该类就不会被实例化
private Singleton(){}
/**
* 单线程用
* 由于多线程下instance可能判断都为空,线程不安全
*/
//开放一个公有方法,判断是否已经存在实例,有返回,没有新建一个在返回
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
/**
* 并发不高情况可以用
*
* 线程安全版本一(空不空都加锁)
*
* 弊端:在方法上加锁,但由于要整个方法完成才会释放锁,但锁的代价过高
* 在高并发时,会有同时很多锁在读代码,锁的代价可能很高
*/
public synchronized static Singleton getInstance1(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
/**
* 双检查锁,一定要加volatile
*
* 线程安全版本二(空才加锁)
* 锁前不检查:每次都加锁
* 锁后检查锁后不检查:多线程下可能加好几次锁,创建好几次对象
* 锁前锁后都检查:多线程下可能加好几次锁,但只会创建一个对象
*
* 弊端:内存读写reorder不安全(会导致双检查锁的失效)
*
*/
// 同步代码块:
public static Singleton getInstance2(){
if(instance == null){
synchronized (syncLock){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
// synchronized同步块括号中的锁定对象是采用的一个无关的Object类实例,而
// 不是采用this因为getInstance是一个静态方法,在它内部不能使用未静态的或
// 者未实例的类对象,因此也可以用下面的方法来实现
public static Singleton getInstance3(){
if(instance == null){
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
/**
* 正常:先分配内存,再调用构造器,,然后把内存地址给instance
* reorder:(可能)先分配内存,再把内存地址直接给instance,最后才调用构造器
* 例:先分配内存0x1234,instance-->0x1234,此时instance!=null。 构造器还没调呢
* 得到的对象不能用,拿到的只是一个原生的内存,对象只有内存地址、状态不对
* 解决方法:在实例变量加 volatile : Java的解决方式
*/
}
② 饿汉模式
/**
* 饿汉模式:加载的时候就被初始化
*/
public class SingletonHungry {
//设立静态变量,直接创建实例
private static SingletonHungry instance = new SingletonHungry();
//让构造函数为 private,这样该类就不会被实例化
private SingletonHungry(){}
// 返回对象
public static SingletonHungry getInstance(){
return instance;
}
}
③ 内部类模式
/**
* 不用加锁也能实现懒加载
*/
public class SingletonInner {
private SingletonInner(){
System.out.println("inner singleton");
}
/**
* 内部类
*/
private static class Inner{
private static SingletonInner instance = new SingletonInner();
}
public SingletonInner getSingletonInner(){
return Inner.instance;
}
}
如果说懒汉式是时间换空间,那么饿汉式就是空间换时间
饿汉式虽然节省了时间,但是浪费了空间
场景:在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率
之前的工厂方法和这个相比:工厂模式绕过new是为了避免new带来的松耦合。而Singleton绕过构造器是解决性能的问题。