单例模式
一、什么是单例模式:
单例模式是一种对象创建形模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。
其实,GoF对单例模式的定义是:保证一个类、只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。
二、为什么要使用单例模式:
在应用开发中,我们常常有以下 需求:
1、在多个线程之间,比如servlet环境,共享同一个资源或者操作同一个对象
2、在整个程序空间使用全局变量,共享资源
3、大规模系统中,为了性能的考虑,需要节省对象的创建时间等等。
因为Singleton模式可以保证为一个类只生成唯一的实例对象,所以这些情况,Singleton模式就派上用场了。
三、单例模式实现
1.饿汉式。
2.懒汉式。
3.双重检查。
四、实现
假如我们要得到一个对象的实例,通常是这么做的:
package com.meritit;public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
package com.meritit;public class MainClass { public static void main(String[] args) { Person person1 = new Person(); Person person2 = new Person(); person1.setName("zhangsan"); person2.setName("lisi"); System.out.println(person1.getName()); System.out.println(person2.getName()); }}要实现单例,必须私有化构造函数,你可能会想到这么实现单例:
package com.meritit;public class Person { private String name; /** * 私有化构造 */ private Person(){ } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 使用静态方法获得对象实例 * @return */ public static Person getPerson(){ return new Person(); } }这样在每次使用该类的静态方法得到实例的时候都会创建一个新实例,这显然不是单例!,下面我们就用饿汉式来实现单例模式。
package com.meritit;public class Person { //创建一个Person实例常量 private static final Person person = new Person(); private String name; /** * 私有化构造 */ private Person(){ } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 使用静态方法获得对象实例 * @return */ public static Person getPerson(){ return person; } }上面这种就是饿汉式,下面我们再看一下怎么用懒汉式来实现:
package com.meritit;/** * 懒汉式单例模式 * @author Administrator * */public class Person { private static Person person; private String name; /** * 私有化构造 */ private Person(){ } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 提供一个全局的静态方法 * @return */ public static Person getPerson(){ //如果没有实例则创建实例 if(person == null){ person = new Person(); } return person; } }饿汉式在类加载的时候就创建了对象的实例,而懒汉式是在调用提供实例的静态方法时才创建(延迟加载)。那么上面的懒汉式是否能保证只提供一个实例?答案是在单线程下是肯定的,但是在多线性下就不能保证了。
如何解决懒汉式在多线程下的问题呢,使用同步方法,请看下面代码:
package com.meritit;/** * 懒汉式单例模式 * @author Administrator * */public class Person { private static Person person; private String name; /** * 私有化构造 */ private Person(){ } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 提供一个全局的静态方法,并使用同步方法 * @return */ public static synchronized Person getPerson(){ //如果没有实例则创建实例 if(person == null){ person = new Person(); } return person; } }上面的方法虽然解决了懒汉式多线程下的单例模式,但是还不够好,因为我们只需要同步创建实例的语句,而不需要同步整个方法,当第一个线程进来后person==null然后创建实例、第二个线程进来后person!=null就不会创建实例。所以只需同步person = new Person().这样可以提高程序的运行效率。
package com.meritit;/** * 懒汉式单例模式 * @author Administrator * */public class Person { private static Person person; private String name; /** * 私有化构造 */ private Person(){ } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 提供一个全局的静态方法,并使用同步方法 * @return */ public static Person getPerson(){ //如果没有实例则创建实例 if(person == null){ synchronized(Person.class){ person = new Person(); } } return person; } }这样做就没有问题了吗?答案是“no"
当第一个线程执行到person==null后进入同步块,执行person=new Person()的时候第二个线程判断person==null,这时候当第一个线程执行完new Person()后第二个线程紧接着会执行new Person().
怎么解决这个问题呢?要效率还是要安全?????
齐声说“都要!!!!!”
我们发现这样做只所以能够提供效率是因为if(){ }中的语句只会被执行一次,那么我们为何不再给加个判断呢?
package com.meritit;/** * 懒汉式单例模式 * @author Administrator * */public class Person { private static Person person; private String name; /** * 私有化构造 */ private Person(){ } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 提供一个全局的静态方法,并使用同步方法 * @return */ public static Person getPerson(){ //如果没有实例则创建实例 if(person == null){ synchronized(Person.class){ //双重判断 if(person == null){ person = new Person(); } } } return person; } }这就是所谓的双重检查.
源代码下载: