首页 > 代码库 > JAVA设计模式--单例模式

JAVA设计模式--单例模式

单例模式可以当做一种编程的技巧,咱们先说理论再说代码

单例模式三个关键点:

1)、某个类只能有一个实例

2)、该类必须自行创建这个实例

3)、该类必须自行向整个系统提供这个实例

应用场景:

1)、window的任务管理器就是很典型的单例模式,你肯定不能同时打开两个任务管理器

2)、数据库连接池技术一般采用的都是单例模式,因为数据库连接是一种数据库资源。系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的

效率损耗,这种效率上的损耗还是非常昂贵的,用单例模式来维护,就可以大大降低这种损耗。

3)、我们在进行开发时对于配置文件的读取一般也是采用单例模式,因为配置文件中的内容是全局共享的资源。

4)、多线程的线程池设计一般也要考虑单例模式,线程池能方便对池中线程的控制。

5)、网站的统计类信息,一般也是采用单例模式,否则难以同步控制,例如统计我的博客的访问量。

6)、我们开发应用程序的日志功能也是采用的单例模式,因为我们只能有一个实例去追加日志信息,否则不好控制。

单例模式的几类写法:

1)饿汉式模式

怎么理解呢,饿了吗,所以我们做饭要非常着急,这里也就是说,当类被加载的时候该类就已经将自己的实例创建出来了。

这也是空间换取时间的典型应用,怎么说呢? 我们在类加载的时候就实例化了这个对象,占用了内存空间,但是我们在用到这个对象的时候就不用去实例化了,

直接拿去用就可以了,也就节省可时间,这也就是空间换取时间。

ps:记得第一份工作(还在大三的时候)面试的时候面试官就让我举出我做过的项目中时间换取空间和空间换取时间的典型应用,给我问懵了。

代码:

package chc.singleton;
/**
 * 饿汉式单例模式类
 * @author haicheng.cao
 * @time 2014.09.02 22:40
 */
public class EagerSingleton {
	//类加载的时候就创建了自身的实例对象--饿汉式(空间换取时间)
	public static EagerSingleton eagerSingleton=new EagerSingleton();
	
	/**
	 * 显示的私有构造方法,防止其他类创建本类实例
	 */
	private EagerSingleton(){
		
	}
	
	/**
	 * 静态工厂方法,其他类通过调用该方法来获取本类的实例对象
	 */
	public static EagerSingleton getEagerSingleton(){
		return eagerSingleton;
	}
}

2)懒汉式模式

这个怎么理解呢?懒人吗,举个例子,一个人马上要去面试的时候才开始写简历,就是说对象实例要用的时候才去创建。在这里也就是说在类加载的时候并没有创

建本类的实例对象,而是在其他类在第一次调用的时候才去创建。

这也是典型的时间换取空间的应用,就是嘛,类加载的时候没有创建对象,节省了内存,也就是节省了空间,调用的时候需要判断一下这个类的实例对象是否存

在,浪费了时间,这也就是时间换取空间。

代码:

package chc.singleton;
/**
 * 懒汉式单例模式类
 * @author haicheng.cao
 * @time 2014.09.02 23:05
 */
public class LazySingleton {
	//类加载的时候并没有创建自身实例化对象
	public static LazySingleton lazySingleton=null;
	
	/**
	 * 显示的私有构造方法,防止其他类创建本类实例
	 */
	private LazySingleton(){
		
	}
	
	/**
	 * 静态工厂方法,其他类通过调用该方法来获取本类的实例对象
	 */
	public static synchronized LazySingleton getLazySingleton(){
		//第一次被调用的时候创建自身实例对象
		if(lazySingleton==null){
			lazySingleton=new LazySingleton();
		}
		return lazySingleton;
	}
}

注意:我们给getEasySingleton()方法加了同步关键字,可以保证线程安全。但是这样做比较影响程序的性能,我们能不能改善一下呢?看下面的:

3)双重检查加锁

双重检查加锁机制的意思就是:我们在调用getEasySingleton()方法的时候不同步,进入方法内我们判断一下实例对象是否存在,如果不存在我们在进入同步代

码块,这是第一重检查,进入同步块后再进行判断,判断实例是否存在,如果不存在再创建这个对象的实例,这就是第二重检查。这样,就只有第一次调用的时候

执行了一次同步代码块,其余的时候就不需要同步了,提升了程序的性能。

代码:

package chc.singleton;
/**
 * 双重检查加锁,针对懒汉式提升性能
 * @author haicheng.cao
 * @time 2014.09.02 22:40
 */
public class TwoCheck {
    private volatile static TwoCheck twoCheck = null;
    
    private TwoCheck(){
    	
    }
    
    public static TwoCheck getInstance(){
        //先检查实例是否存在,如果不存在才进入下面的同步块
        if(twoCheck == null){
            //同步块,线程安全的创建实例
            synchronized (TwoCheck.class) {
                //再次检查实例是否存在,如果不存在才真正的创建实例
                if(twoCheck == null){
                	twoCheck = new TwoCheck();
                }
            }
        }
        return twoCheck;
    }
}

JAVA设计模式--单例模式