单例模式(Singleton Pattern)
单例模式(Singleton Pattern)
定义
确保一个类只有一个实例,并提供一个全局访问点。
类图
懒汉式
所谓的懒汉式就是延迟对象实例化
代码
public class Singleton {
private static Singleton singleton;
private Singleton(){
System.out.println("我是单例模式独一无二的对象");
};
//对象只有在调用了该方法后才进行实例化
public static Singleton getInstance() {
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
单线程测试
public class SingletonTest {
public static void main(String[] args) {
Singleton singleton =Singleton.getInstance();
singleton = Singleton.getInstance();
singleton = Singleton.getInstance();
singleton = Singleton.getInstance();
}
}
多线程测试
public class SingletonTest {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
Singleton singleton =Singleton.getInstance();
}
}.start();
new Thread(){
@Override
public void run() {
Singleton singleton =Singleton.getInstance();
}
}.start();
new Thread(){
@Override
public void run() {
Singleton singleton =Singleton.getInstance();
}
}.start();
new Thread(){
@Override
public void run() {
Singleton singleton =Singleton.getInstance();
}
}.start();
new Thread(){
@Override
public void run() {
Singleton singleton =Singleton.getInstance();
}
}.start();
}
}
测试结果:
出现结果分析
简单改进
//修改getInstance()方法,给它加synchronized 关键字
public synchronized static Singleton getInstance() {
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
但这样子真的好嘛?仔细想想,好像确实对性能的影响很大,因为我们只在第一次执行这个方法时,才真正需要同步,换句话说,一旦设置好singleton变量,就不再需要同步这个方法了,之后调用这个方法,同步都是一种累赘。
改善多线程的方法
- 如果getInstance()的性能对应用程序不是很关键,那就不需要改了!
同步getInstance()的方法既简单又有效。但你必须知道,同步一个方法可能造成程序执行效率下降100倍。应此,如果将getInstance()的程序使用在频繁运行的地方,你可能就得重新考虑了! - 使用“急切”(饿汉式)创建实例,而不用延迟实例化(懒汉式)的做法。
如果程序总是创建并使用单例实例,或者在创建和运行时方面的负担不太繁重,可以使用此方法
public class Singleton {
private static Singleton singleton =new Singleton();
//在静态初始化器中创建单例。这段代码保证了线程安全。
private Singleton(){
System.out.println("我是单例模式独一无二的对象");
};
public static Singleton getInstance() {
return singleton;//已经有实例了,直接使用它
}
}
- 用“双重检索加锁”,在getInstance()中减少使用同步
如果性能是你关注的重点,那么这个做法可以帮你大大减少getInstance()的时间耗费。
public class Singleton {
private volatile static Singleton singleton;
private Singleton(){
System.out.println("我是单例模式独一无二的对象");
};
public static Singleton getInstance() {
if(singleton==null){//检查是否已经创建
synchronized(singleton){//未创建,则进入同步
if(singleton==null){//再检查一次,如果仍是null,才创建实例
singleton = new Singleton();
}
}
}
return singleton;
}
}
补充:
volatile关键词确保singleton变量被初始化成Singleton实例时,多线程正确地处理singleton变量
总结
懒汉式:延迟了实例化,在单线程模式下,能达到单例模式的效果,但在多线程环境时,就会出现意想不到的bug,简单的在getInstance()方法上加synchronized,会降低多线程的执行效率,可以利用“双重检索加锁”的方法,提高效率
饿汉式:在静态初始化器中创建单例,如果程序总是创建并使用单例实例,或者在创建和运行时方面的负担不太繁重,可以使用此方法,它将不再使用延迟实例化的方法创建单例,依赖JVM在加载这个类时,马上创建此唯一单例,保证在任何线程访问是singleton变量时,一定先创建此实例!