创建型模式——单例模式

创建型模式——单例模式

(一)概述

单例模式是最简单的设计模式之一,属于创建型模式,它提供了一种特殊的创建对象的方式,确保全局中只有单个对象被创建。这个设计模式主要目的是想在整个系统中只能出现类的一个实例,即一个类只有一个对象。

在这里插入图片描述
单例模式可以非常有效地节约资源,主要有以下两点:

  1. 由于频繁使用已经创建完成的对象,可以省略多次创建对象所花费的时间,这对于那些重量级的对象而言,效果非常明显。
  2. 因为不需要频繁创建对象,GC压力也减轻了,而在GC中会有STW(stop the world),从这一方面也节约了GC的时间。

但是,单例模式很多时候不得不考虑多线程并发的问题。虽然单例模式本身非常简单,但是加上并发问题之后,使得它变为一种非常复杂且容易出错的设计模式。下面我们来看看单例模式的实现方式:

(二)饿汉模式实现

public class Singleton {  
	private static Singleton instance = new Singleton();  
	private Singleton (){}
	public static Singleton getInstance() {  
		return instance;  
	}  
 }  

饿汉模式是单例模式最简单也是最基础的实现方式之一。这种方式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。 这种方式基于类加载机制避免了多线程的同步问题,因此非常通俗易懂。

(三)非线程安全的懒汉模式实现

public class Singleton {  
	private static Singleton instance;  
	private Singleton (){}   
	public static Singleton getInstance() {  
		if (instance == null) {  
    		instance = new Singleton();  
		}  
		return instance;  
	}  
}  

这是最基础的懒加载模式的单例模式。我们可以发现与饿汉模式相比,懒汉模式将对象创建时机放到了使用时,当第一次使用这个对象时,才会对对象进行初始化。因此,这种方式能更有效的利用资源。但是,重点来了,这段代码无法在多线程的环境下正常运行(关键在于创建对象时可能出现的指令重排序,这里就不展开了)。

(四)线程安全的懒汉模式实现

public class Singleton {  
	private static Singleton instance;  
	private Singleton (){}
	public static synchronized Singleton getInstance() {  
		if (instance == null) {  
    		instance = new Singleton();  
		}  
		return instance;  
	}  
}  

我们可以看到这个懒汉模式和上面的只有一个区别,就是多了一个synchronized关键字,这个关键字使得在同一时间只有一个线程能进入getInstance()的代码块中,避免了多线程情况下的问题。但是,synchronized造成了很多不必要的同步开销,使得这段代码的效率极为低下。因为可能出现问题的时候只是创建对象时,显然我们的对象只会创建一次,而synchronized使得每次使用对象时都要进行同步,造成极大的资源浪费。因此,这种实现方式基本可以弃用。

我们可以思考一下为什么这段代码效率低,就能找到改进方法。按照道理,我们只需要在第一次创建对象时进行同步,但是这段代码实际将后面每次使用对象的过程也进行了同步。因此,我们可以认为他的封锁粒度过大,因此我们要着手降低封锁的粒度。

(五)双重检查锁的懒汉模式实现

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
	    if (singleton == null) {  
	        synchronized (Singleton.class) {  
		        if (singleton == null) {  
		            singleton = new Singleton();  
		        }  
	        }  
	    }  
	    return singleton;  
    }  
}  

synchronized同步块里面能够保证只创建一个对象。但是通过在synchronized的外面增加一层判断,就可以在对象一经创建以后,不再进入synchronized同步块,这种方案不仅减小了锁的粒度,保证了线程安全,同时性能方面也得到了大幅提升。同时这里要注意volatile不能少,这个很关键,volatile一般用于多线程的可见性,但是这里是用来防止指令重排序的。

(六)静态内部类懒汉模式实现

public class Singleton { 
	private Singleton(){}
  	public static Singleton getInstance(){  
    	return SingletonHolder.sInstance;  
	}  
	private static class SingletonHolder {  
    	private static final Singleton sInstance = new Singleton();  
	}  
} 

这是双重检查锁的懒汉模式的替代,一种比较巧妙的实现方式。第一次加载Singleton类时并不会初始化sInstance,只有第一次调用getInstance方法时虚拟机才会加载SingletonHolder并初始化sInstance对象,这样不仅能确保线程安全也能保证唯一性,所以推荐使用静态内部类单例模式。

(七)基于枚举类的饿汉模式实现

public enum Singleton{
    INSTANCE;
}

默认枚举实例的创建是线程安全的,所以不需要担心线程安全的问题。通过枚举类,能自动避免序列化/反序列化攻击,以及反射攻击(枚举类不能通过反射生成)。

2020年7月9日

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值