单例模式
单例(Singleton)模式:某采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
概括:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式
单例模式特点:
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点
饿汉式单例模式
类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了
饿汉式(静态常量)
public class HungryType1 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1= Singleton.getInstance();
System.out.println(instance == instance1);
}
}
class Singleton{
private Singleton(){}
private static final Singleton singleton = new Singleton();
private static Singleton singleton;
public static Singleton getInstance() {
return singleton;
}
}
饿汉式(静态代码块)
public class HungryType1 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1= Singleton.getInstance();
System.out.println(instance == instance1);
}
}
class Singleton{
private Singleton(){}
private static Singleton singleton;
static {
singleton = new Singleton();
}
public static Singleton getInstance() {
return singleton;
}
}
优点:这种饿汉式单例模式写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题
缺点:没有达到Lazy Loading的效果;可能会造成内存的浪费
懒汉式单例模式
类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例
懒汉式( 线程安全)
volatile + 双重检测机制
public class UltimateLazyMan {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1= Singleton.getInstance();
System.out.println(instance == instance1);
}
}
class Singleton{
private Singleton() { }
/*
*
* 1、memory = allocate() 分配对象的内存空间
* 2、ctorInstance() 初始化对象
* 3、instance = memory 设置instance指向刚分配的内存
* */
//单例对象 volatile + 双重检测机制 -> 禁止指令重排
private static volatile Singleton singleton = null;
// 静态的工厂方法
public static Singleton getInstance() {
if (singleton == null) {// 双重监测机制
synchronized (Singleton.class){// 同步锁
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
- Double-Check概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。
- 这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),直接return实例化对象,也避免的反复进行方法同步.
- 线程安全;延迟加载;效率较高
懒汉式( 内部静态类)
利用静态内部类的加载机制:
当类被装载的时候,静态内部类不会被立即装载;只有用到静态内部的属性或者调用方法时,才会导致静态内部类被装载,且只会装载一次,被装载的时候线程是安全的
public class LazyManStaticInner {
public static void main(String[] args) {
SingletonInstance instance = SingletonInstance.getSingletonInstance();
SingletonInstance instance1= SingletonInstance.getSingletonInstance();
System.out.println(instance == instance1);
}
}
class SingletonInstance {
private SingletonInstance() {}
private static final class SingletonInnerInstance{
private static final SingletonInstance singletonInstance = new SingletonInstance();
}
public static SingletonInstance getSingletonInstance(){
return SingletonInnerInstance.singletonInstance;
}
}
- 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
- 静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
- 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
- 优点:避免了 线程不安全,利用静态内部类特点实现延迟加载,效率高
使用场景及细节说明
使用场景
- 1、要求生产唯一序列号。
- 2、代码级别的全局的JVM缓存。(有状态的单例)
- 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
细节说明
- 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
- 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new