单例模式是一种对象创建型模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。
其实,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;
}
}
至此,三种实现方式如下所述。
单例保证了一处程序内,只有一个实例。