学习设计模式系列之——单例模式(Java)
单例模式是一种对象创建型模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。
?其实,GoF对单例模式的定义是:保证一个类、只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。
在单例模式中,有三种实现方式:1、饿汉式;2、懒汉式;3、双重检查,下面我们分别来看这三种方式。
?
/** * * Person.java * sxt_Singleton * 2010-7-7 下午09:22:32 *//** * 饿汉式单例模式 * 线程永远是安全的 * 在单线程和多线程中可以保持单例 * 2010-7-7 下午09:22:32 */public class Person {public static final Person person=new Person();/** * 姓名 */private String name;/** * @return the name */public String getName() {return name;}/** * @param name the name to set */public void setName(String name) {this.name = name;}/** * private Constructor * Person.java * 2010-7-7 下午09:25:52 */private Person() {}/** * 提供一个全局的静态方法 */public static Person getPerson(){return person;}}主类的访问是:/* * 饿汉式 */Person per=Person.getPerson();Person per2=Person.getPerson();per.setName("xiao");per2.setName("dai");System.out.println(per.getName());System.out.println(per2.getName());?上述的是饿汉式,接下来我们看懒汉式
/** * 懒汉式单例模式 * 不能保证多线程中保证单例 * 2010-7-7 下午09:22:32 */public class Person2 {/** * 姓名 */private String name;private static Person2 person;/** * @return the name */public String getName() {return name;}/** * @param name the name to set */public void setName(String name) {this.name = name;}/** * private Constructor * Person.java * 2010-7-7 下午09:25:52 */private Person2() {}/** * 提供一个全局的静态访问方法 */public static Person2 getPerson(){/* * 当A线程运行到if(person==null)时,person为null,因此执行person= new Person2(); * 此时,B线程也正好执行到if(person==null),但A线程还没有产生Person2的实例 * 因此B线程又一次执行了person= new Person2() */if(person==null){person= new Person2();}return person;}}??由于懒汉式不能保证线程的安全性,因为我们对其进行改造,改造过后的代码如下:
/** * 对Person2进行改造之后的Person3 * 2010-7-7 下午09:22:32 */public class Person3 {private static Person3 person;/** * 姓名 */private String name;/** * @return the name */public String getName() {return name;}/** * @param name the name to set */public void setName(String name) {this.name = name;}/** * private Constructor * Person.java * 2010-7-7 下午09:25:52 */private Person3() {}/** * 加入同步关键字,解决Person2多线程访问的问题 * 对整个方法同步的时候,效率比较低,解决方法看Person4 * 对整个方法时行同步,那么if(person==null)每次都要执行 * 提供一个全局的静态方法 */public static synchronized Person3 getPerson(){if(person==null){person= new Person3();}return person;}}?在Person3中,我们加入了同步关键字,但是效率不高,经过我们的改造,产生了Person4,代码如下:
/** * 双得检查单例模式 * 在单线程和多线程中可以保持单例 * 2010-7-7 下午09:22:32 */public class Person4 {private static Person4 person;/** * 姓名 */private String name;/** * @return the name */public String getName() {return name;}/** * @param name the name to set */public void setName(String name) {this.name = name;}/** * private Constructor * Person.java * 2010-7-7 下午09:25:52 */private Person4() {}/** * 加入同步关键字,解决Person2多线程访问的问题 * 同步关键没有在方法级加,是因为考虑到效率问题 * 采用同步代码块,保证了if(person==null)里面的代码只运行一次 * 例如:当A线程执行到if(person==null),此时person为null,因为执行同步代码块中的代码 * 同时,B线程也执行到了if(person==null),而A还没有产生Person4的实例,因此也执行同步代码块中的代码 * A结束以后,B开始,可是,person已经产生,那么B线程就会再执行person=new Person4() * 那么在接下来的线程,如C、D线程,判断if(person==null)时,person已经不在是null了 * 那么方法直接返回 * 所在相对于Person3中的代码来说效率高 * 提供一个全局的静态方法 */public static Person4 getPerson(){if(person==null){/* * 同步代码块只会执行一次 * 因此相对于Person3,效率比较高 */synchronized(Person4.class){if(person==null){person=new Person4();}}}return person;}}?至此,三种实现方式如下所述。
单例保证了一处程序内,只有一个实例。
1 楼 mercyblitz 2010-07-08 引用public class Person4 {
private static Person4 person;
你的double lock check 是错的,应该需要加一个volatile给Person4单例对象。