在某些情况下,有些对象只需要一个就够了,即每个类只需要一个实例,简单来说单例模式的作用就是保证在整个应用程序的生命周期过程中,任何时刻,单例类的实例都只存在一个,单例模式确保某一个类只有一个实例,而且自行实例化,并向整个系统提供这个实例,单例模式在类中保存了它唯一实例的全局,同时还提供了访问该唯一实例的全局访问点
使用场景
资源共享情况下,避免资源操作是造成的性能损耗,在控制资源的情况下,方便资源之间相互通信,windows 的 Task Manager(任务管理器)就是典型的单例模式,还有我们项目中用到的日志打印,一般也采用单例模式实现,因为只能有一个实例去操作,否则内容不好追加,好了不说废话了,开始演绎
饿汉式
- /**
- * 单例模式示例(饿汉式)
- */
- public class SingletonTest {
- //定义私有构造方法
- private SingletonTest() {
- }
- // 将自身的实例对象设置为一个属性,并加上static和final修饰符
- private static final SingletonTest instance = new SingletonTest();
- // 静态方法返回该类的实例
- public static SingletonTest getInstance() {
- return instance;
- }
- }
/**
* 单例模式示例(饿汉式)
*/
public class SingletonTest {
//定义私有构造方法
private SingletonTest() {
}
// 将自身的实例对象设置为一个属性,并加上static和final修饰符
private static final SingletonTest instance = new SingletonTest();
// 静态方法返回该类的实例
public static SingletonTest getInstance() {
return instance;
}
}
这是单例模式实现之饿汉式,大家可以看到首先定义了一个构造函数并且私有化,这样就防止了使用 new 关键字来创建实例,饿汉式写起来比较简单,而且也不存在多线程同步问题,避免了 synchronized 所造成的性能问题,但缺点也很明显,当类 SingletonTest 被加载的时候,会初始化 static 的 instance,静态变量被创建并且分配内存空间,即使你可能还没有用到这个实例,但这以后 static 的instance 便一直占据这这段内存,只有当类被销毁时这这段内存才会被释放,因此饿汉式相对会比较消费内存
懒汉式
- /**
- * 单例模式示例(懒汉式)
- */
- public class SingletonTest {
- //定义私有构造方法
- private SingletonTest() {
- }
- //定义一个SingletonTest类型的静态instance变量
- private static SingletonTest instance;
- // 静态方法返回该类的实例
- public static SingletonTest getInstance() {
- if (instance == null) {
- instance = new SingletonTest();
- }
- return instance;
- }
- }
/**
* 单例模式示例(懒汉式)
*/
public class SingletonTest {
//定义私有构造方法
private SingletonTest() {
}
//定义一个SingletonTest类型的静态instance变量
private static SingletonTest instance;
// 静态方法返回该类的实例
public static SingletonTest getInstance() {
if (instance == null) {
instance = new SingletonTest();
}
return instance;
}
}
在这里我们同样先定义一个私有的构造函数以防止 new 关键字来创建实例,需要注意的是我们在定义 SingletonTest 类型的静态变量instance 时是没有 final 的,因为我们不需要初始化,写起来也比较简单,当 SingletonTest 类被加载时,静态变量 instance 被创建并且分配内存空间,当 getInstance 方法被第一次调用的时候初始化 instance 变量并分配内存,因此在特定情况下节约了内存,但我们在仔细去思考时会发现,这段代码是存在 bug 的,你因该也想到的,对,就是在多线程中是不安全的,我们来仔细分析一下如下的代码
- public static SingletonTest getInstance() {
- if (instance == null) {
- instance = new SingletonTest();
- }
- return instance;
- }
public static SingletonTest getInstance() {
if (instance == null) {
instance = new SingletonTest();
}
return instance;
}
试想如果现在有两个线程同时在执行 getInstance 方法,第一个线程刚执行完第二行,还没有来得及执行第 3 行,这时候第二个线程也至执行到了第二行,它会发现 instance 还是空,于是也会去创建一个对象,这样就违背我们的初衷只创建一个实例对象,这里就会创建两个对象,那么我们来对代码进行修改如下
- public synchronized static SingletonTest getInstance() {
- if (instance == null) {
- instance = new SingletonTest();
- }
- return instance;
- }
public synchronized static SingletonTest getInstance() {
if (instance == null) {
instance = new SingletonTest();
}
return instance;
}
可以看到我们给方法加上了 synchronized 关键字,这样我们就加上了同步锁,保证只有一个线程来执行 getInstance 里面的方法,这样就解决了线程安全问题,不会再创建多个实例,但我们在仔细看一下这段代码,还是有问题的,当然这并不是 bug,只是性能上的问题,会影响运行效率,试想假如我们的 SingletonTest 实例已经创建,但我们还是要每次都必须去执行同步锁方法,这样显然是影响性能的,这里举个例子:大家可能都做过地铁,特别是身在北京,如果我们身上没有带行李的话是不需要通过安检通道的,可以直接进站乘车,只有我们携带了行李才需要走安检通道,但如果有一天地铁官方突然公布,不管我们是否携带行李都必须通过安检通道,这样显然是不合理的,这样人少的时候还好,当遇到上下班高峰期,是不是得被堵掉,同理到我们的代码里就是不管实例存在还是不存在,只要执行该方法都必须排队通过,这样很显然也是不太合理的,我们修改如下
- public static SingletonTest getInstance() {
- if (instance == null) {
- synchronized (SingletonTest.class) {
- if (instance == null) {
- instance = new SingletonTest();
- }
- }
- }
- return instance;
- }
public static SingletonTest getInstance() {
if (instance == null) {
synchronized (SingletonTest.class) {
if (instance == null) {
instance = new SingletonTest();
}
}
}
return instance;
}
这样我们我们在调用 getInstance 方法时就不会受到影响,进入方法后我们再去判断实例是否为空,如果为空再去创建对象,这时候再加同步锁,对性能和效率会更好,最后总结一句,单例模式就是保证一个类在应用中只有一个对象,并且向外提供一个访问它的公共方法,对单例模式的理解就说这些,如果有理解不到位的希望指出