设计模式-单例模式
1.单例模式简介
单例模式的类型
单例模式分为4中类型:饿汉式,懒汉式,静态内部类懒汉模式,枚举模式
2.饿汉模式
饿汉模式在类加载时就创建好类的实例对象,获取时直接返回已经创建好的对象,由于提前准备好了需要返回的对象,在调用时性能较好
public class Singleton {
//类加载时就创建好类实例对象
private static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
3.懒汉模式
懒汉模式在第一次被调用的时候,才会创建类的实例对象,节省内存
以下摘抄自 --- 《java并发编程实战》
竞态条件:当某个计算的正确性取决于多个线程的交替执行时序时,那么就会发生竞态条件。
换句话说,就是正确的结果要取决于运气。
最常见的静态条件类型就是“先检查后执行”操作,即通过一个可能失效的观测结果来决定下一步的动作
/**
* 该懒汉单例模式有线程安全问题
*/
public class Singleton {
private static Singleton singleton = null;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
//在这里,可能多个线程都通过了判空条件,都对singleton进行了初始化。各个线程会的到不同的对象,违反了单例模式的初衷。
singleton = new Singleton();
}
return singleton;
}
}
代码改进(线程安全)
由于未加锁的懒汉模式有线程安全问题,所以对以上代码进行加锁改进
public class Singleton {
private static Singleton singleton = null;
private Singleton() {
}
//加锁后,所有线程在进入该方法时都要执行加锁和解锁的动作,效率较低
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
代码改进(性能优化)
该方式也叫 双重锁懒汉模式
public class Singleton {
/**
* 由于jvm存在乱序执行功能,如果不加volatile修饰,也会出现线程不安全的情况
* <p>
* singleton = new Singleton();分为3个步骤
* 1.在堆内存开辟内存空间,此时singleton的参数都是默认值
* 2.初始化singleton的参数
* 3.将对象指向内存空间
* <p>
* 如果不加volatile修饰符,实例化的步骤可能会被重排序为 1,3,2
* <p>
* 假如线程1 第一个进入该方法,按照重排序的步骤执行到3时,
* 此时切换到线程2,判断singleton是否为空时,由于singleton已经指向了内存空间,所以该对象不为空,
* 直接return的对象内的成员变量可能没有经过初始化。
**/
private volatile static Singleton singleton = null;
private Singleton() {
}
public static Singleton getInstance() {
//第一次判断 是为了筛除不必要的加锁和解锁动作
if (singleton == null) {
synchronized (Singleton.class) {
//第二次判断 是为了线程安全
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
4.静态内部类模式
静态内部类模式利用了类加载过程的安全性,使用内部类创建单例对象
public class Singleton {
private static class SingletonHolder {
private static Singleton INSTANCE = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
静态内部类是线程安全的懒加载单例模式,但是静态内部类模式也有致命的缺陷,无法传参数
5.枚举模式
public enum SingletonEnum {
SINGLETON("ONE");
SingletonEnum(String arg) {
this.arg = arg;
}
}