单例模式
概述
单例模式确保一个类只有一个实例,并提供一个全局访问点
我们常常希望某个对象实例只有一个,不想要频繁的创建和销毁对象,浪费系统资源,最常见的就是IO,数据库的连接,redis连接等对象,完全没有必要创建多个一模一样的对象,一个足矣。
类图:
正如上面类图所示,单例模式就是这么简单,用静态变量instance将实例保存起来,然后调用getInstance()时直接返回instance变量,这就是单例模式。
虽然单例模式简单,但是也不要小看它,其中还有许多细节需要我们注意。
线程安全问题
单例模式代码如下:
public class SingleObject {
private static SingleObject instance;
private SingleObject(){}
public static SingleObject getInstance(){
if(instance == null){
instance = new SingleObject();
}
return instance;
}
}
但是上面代码会有一个问题,当多个线程同时调用 getInstance()
方法时,可能会产生多个instance
实例,因此这种方式并不是真正的单例。
为了解决线程安全问题,我们只需要在getInstance()
方法上使用synchronized
关键字给线程加锁即可。
public class SingleObject {
private static SingleObject instance;
private SingleObject(){}
public static synchronized SingleObject getInstance(){
if(instance == null){
instance = new SingleObject();
}
return instance;
}
}
synchronized
的作用是加锁,当多个线程同时调用getInstance()
时,只有一个线程能进入,其他线程会等待进入的线程出来之后在一一进入,这样就能保证instance
实例是唯一的。这才是真正的单例。
不过这并不是完美的解决方案,只要是锁,必然有性能损耗问题。而且对于上面的代码,其实我们只需要在线程第一次访问时加锁即可,之后并不需要锁,锁给我们带来了系统资源浪费。
所以又有了新的解决方案。上面两种方式都是在getInstance()
方法中创建实例,也就是说在要调用的时候才创建实例,这种方式被称为“懒汉式”,说实话这个词不知道谁给命名的,实在有点难听,让我总是不自觉地想到“老汉”二字。
咱们还是用它的英文名,叫 lazy loading
,也就是延迟加载。
新的解决方案是not lazy loading
,在类加载时就创建好了实例:
public class SingleObject {
private static SingleObject instance = new SingleObject();
private SingleObject(){}
public static SingleObject getInstance(){
return instance;
}
}
这种方式就可以保证实例唯一。
除了上面的几种方式,还有一种叫 double-checked locking
(双重检查加锁)
这种方式主要用到两个关键字volatile
和 synchronized
,synchronized
已经解释过了,就不再多说,而volatile
关键字许多人不了解,没关系,我们先看代码:
public class SingleObject {
private volatile static SingleObject instance;
private SingleObject(){}
public static SingleObject getInstance(){
if(instance == null){
synchronized (SingleObject.class){
if(instance == null){
instance = new SingleObject();
}
}
}
return instance;
}
}
volatile
关键字简单来说就是可以保证instance变量在被其中一个线程new出来时,其他线程可以立即看到结果并正确的处理它。对volatile
有兴趣的朋友可以自行度娘。
这种方式的单例模式可以大大的减少锁所带来的性能损耗。
总结
综合上面所说,一般情况我们使用下面这种方式就可以了,如有特殊需求,也可以使用双重检查加锁方式(其实双重检查加锁方式笔者也没使用过)
public class SingleObject {
private static SingleObject instance = new SingleObject();
private SingleObject(){}
public static SingleObject getInstance(){
return instance;
}
}
优点
使用单例模式,对象在内存中只有一个实例,并且无需频繁的创建和销毁对象,大大的减少了性能的损耗
转载 :https://gitbook.cn/gitchat/column/5b1e3647294fb04d7c22b783/topic/5b1fa54952823b71110333bc