4.13--设计模式之创建型之单例模式(总复习版本)---脚踏实地,一步一个脚印
**
一、什么是单例模式
:**
一个类只有一个实例,且该类能自行创建这个实例的一种模式
单例模式特点:
①单例类只有一个实例对象
②该单例对象必须由单例类自行创建
③单例类对外提供一个访问该单例的全局访问点
二、饿汉式单例:
类一旦加载就创建一个单例,保证在调用getInstance方法之前单例已经存在,这种饿汉式单例会造成空间浪费(线程安全)
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
private Singleton1() {}
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return single;
}
}
三、懒汉式单例:
为了避免内存空间浪费,采用懒汉式单例,即用到该单例对象的时候再创建。
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
private Singleton() {}
private static Singleton single=null;
//静态工厂方法
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
Singleton 通过私有化构造函数,避免类在外部被实例化,而且只能通过 getInstance() 方法获取 Singleton 的唯一实例。
但是懒汉式单例的实现是线程不安全的,在并发环境下可能出现多个 Singleton 实例的问题。
要实现线程安全,有以下三种方式,都是对 getInstance() 这个方法改造,保证了懒汉式单例的线程安全:
1、在 getInstance() 方法上加同步机制:
public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的
2、双重检查锁定:
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
private Singleton() {}
private volatile static Singleton singleton=null;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
1.为什么 getInstance() 方法内需要使用两个 if (singleton == null) 进行判断呢?
假设高并发下,线程A、B 都通过了第一个 if 条件。若A先抢到锁,new 了一个对象,释放锁,然后线程B再抢到锁,此时如果不做第二个 if 判断,B线程将会再 new 一个对象。使用两个 if 判断,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗。
2.volatile 关键字的作用?
volatile 的作用主要是禁止指定重排序。假设在不使用 volatile 的情况下,两个线程A、B,都是第一次调用该单例方法,线程A先执行 singleton = new Singleton(),但由于构造方法不是一个原子操作,编译后会生成多条字节码指令,由于 JAVA的 指令重排序,可能会先执行 singleton 的赋值操作,该操作实际只是在内存中开辟一片存储对象的区域后直接返回内存的引用,之后 singleton 便不为空了,但是实际的初始化操作却还没有执行。如果此时线程B进入,就会拿到一个不为空的但是没有完成初始化的singleton 对象,所以需要加入volatile关键字,禁止指令重排序优化,从而安全的实现单例。
3、静态内部类:
public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
利用了类加载机制来保证初始化 instance 时只有一个线程,所以也是线程安全的,同时没有性能损耗