设计模式—创建型模式之单例模式
设计模式—创建型模式之单例模式
介绍
单例模式说明:一个单一的类,负责创建自己的对象,同时确保系统中只有单个对象被创建。
单例模式特点:
- 某个类只能有一个实例;(构造器私有)
- 它必须自行创建这个实例;(自己编写实例化逻辑)
- 它必须自行向整个系统提供这个实例;(对外提供实例化方法)
单例模式图示如下:
饿汉式
饿汉式,比较简单,代码如下:
public class SingletonObject {
private final static SingletonObject obj = new SingletonObject();
private SingletonObject() {
System.out.println("创建了单例对象");
}
public static SingletonObject getInstance() {
return obj;
}
}
懒汉式—效率低下实现方式1(线程安全)
获取实例的方法是static的,我们可以给整个方法加一个锁,这样锁的对象是整个类,可以保证线程安全:
代码实现如下:
public class SingletonObject {
//懒汉式
private static SingletonObject obj;
//保证构造器私有,外部不能实例化
private SingletonObject() {
System.out.println("创建了单例对象");
}
//这种锁粒度太大,导致效率低
public static synchronized SingletonObject getInstance() {
//懒汉式,如果没有再去创建
if(obj == null) {
obj = new SingletonObject();
}
return obj;
}
}
懒汉式—效率低下实现方式2(线程安全)
我们可以不给整个方法加锁,可以给如下代码块加锁,但是这样的方式效率还是低;
public class SingletonObject {
//懒汉式
private static SingletonObject obj;
//保证构造器私有,外部不能实例化
private SingletonObject() {
System.out.println("创建了单例对象");
}
//但是这样锁粒度还是太大,进入到方法里边再加锁,这样效率还低
public static SingletonObject getInstance() {
synchronized(SingletonObject.class) {
//懒汉式,如果没有再去创建
if(obj == null) {
obj = new SingletonObject();
}
}
return obj;
}
}
懒汉式—线程不安全
我们能否在创建时再加锁呢,于是有了如下的代码:
public class SingletonObject {
//懒汉式
private static SingletonObject obj;
//保证构造器私有,外部不能实例化
private SingletonObject() {
System.out.println("创建了单例对象");
}
//线程不安全
public static SingletonObject getInstance() {
//懒汉式,如果没有再去创建
if(obj == null) {
synchronized(SingletonObject.class) {
obj = new SingletonObject();
}
}
return obj;
}
}
这样的方式是线程不安全的,比如:
- 有两个线程,线程1和线程2都进入到方法中,判断到obj为null;
- 假如线程1先获取到锁,为obj赋值完成,然后方法运行结束,返回obj;
- 然后线程2获取到锁,又把obj赋值一次;此时两次返回的就不是同一个对象了。
懒汉式—双重检查锁
下面的懒汉式设计模式,用了双重检查锁;
public class SingletonObject {
//懒汉式,线程可见性
private volatile static SingletonObject obj;
//首先保证构造器私有,外部不能实例化
private SingletonObject() {
System.out.println("创建了单例对象");
}
/**
* 双重检查锁 + 内存可见性volatile
*/
public static SingletonObject getInstance() {
//懒汉式,如果没有再去创建
if (obj == null) {
synchronized (SingletonObject.class) {
if(obj == null){
obj = new SingletonObject();
}
}
}
return obj;
}
}
方法getInstance()中,如果单例对象为空,才会把方法块加锁,获取到锁的线程创建对象完成并赋值成功,且obj保证了线程可见性,其他线程便可以感知到obj不为null,就不会再创建赋值了。