设计模式笔记之单例模式
前言
在一个系统中某些对象只需要一份实例,比方线程池、缓存、日志对象、某些资源文件对象等。这类对象如果有了多个实例就会带来许多问题,例如程序行为异常、资源使用过量、执行结果不一致等。
为了实现程序中的某个对象只有一份实例,常见的实现方式有定义全局变量、开发人员之间约定和单例模式等。
单例模式详解
概念
单例模式确保一个类只有一个实例,并提供一个全局访问点。
为了保证只有一个实例,必须把构造方法私有化不能随意创建对象,并且需要提供一个实例化对象的公共方法。具体的代码实现如下:
package cn.lzz.hf.single;
public class SingleCounter {
private Long count;
//静态实例变量
private static SingleCounter singleCounter;
private SingleCounter(){
this.count=0L;
}
/**
* 获取实例
* @return
*/
public static SingleCounter getInstance(){
if(null==singleCounter){
singleCounter=new SingleCounter();
}
return singleCounter;
}
public void addAccount(){
this.count++;
}
public Long getCount() {
return count;
}
}
这样如果需要得到实例必须getInstance方法调用获取不能随意创建实例,而且在程序的任何地方都能调用getInstance方法得到实例。
上文的代码只适用于单线程情况。如果有多个线程同时访问getInstance方法获取实例,那么就会存在线程之间获取的实例对象不一致的情况。例如第一个线程判断实例变量等于空需要创建一个新的实例对象,就在它刚刚准备创建对象时需要让出CPU等待;这时第二个线程同样判断实例变量等于空,所以创建了一个新的对象返回。当线程一再次执行又会创建一个新的实例,这时候两个线程得到的实例变量就会不一样。
为了解决单例模式多线程的问题,下面介绍两种常用的解决方式。
饿汉式
package cn.lzz.hf.single;
public class SingleCounter {
private Long count;
//静态实例变量
private static SingleCounter singleCounter=new SingleCounter();
private SingleCounter(){
this.count=0L;
}
/**
* 获取实例
* @return
*/
public static SingleCounter getInstance(){
return singleCounter;
}
public void addAccount(){
this.count++;
}
public Long getCount() {
return count;
}
}
饿汉式在代码初始化时创建对象,这样保证只有一份实例,不会有多线程问题。但是如果创建该实例需要大量资源和时间,会影响程序启动效率。
懒汉式
package cn.lzz.hf.single;
public class SingleCounter {
private Long count;
//静态实例变量
private static volatile SingleCounter singleCounter;
private SingleCounter(){
this.count=0L;
}
/**
* 获取实例
* @return
*/
public static SingleCounter getInstance(){
if(null==singleCounter){
synchronized (SingleCounter.class) {
if(null==singleCounter){
singleCounter=new SingleCounter();
}
}
}
return singleCounter;
}
public void addAccount(){
this.count++;
}
public Long getCount() {
return count;
}
}
懒汉式在需要对象时候才实例化,用双重判断加锁的方式,先判断实例是否创建,如果实例没有创建,用synchronize关键字保证代码同步。在同步代码块中再一次判断是否已经创建实例变量。同时用volatile关键字修饰静态实例变量,保证多个线程得到的实例对象是同一份。
总结
多线程情况下单例模式的实现方式有很多种,在这就不一一介绍。不管使用那种方式都需要考虑性能和资源然后选择最优的解决方法。