您的位置:首页 > 其它

设计模式的应用场景(5)--单例模式

2017-08-28 20:00 274 查看

单例模式

定义:单例模式确保一个类只有一个实例并且该实例必须自动创建,具有唯一的全局访问点。

优点:客户端智能通过一个公共的调用点对类实例进行调用。

缺点:实现单例模式的类不能被别的类继承。

使用时机:当系统要求一个类只有一个实例时候。

单例模式在程序设计中使用的频率非常之高,其设计的目的是为了在程序中提供唯一一个对象(保证只被构造一次),例如写入日志的log对象,windows的任务管理器实现(只能打开一个)。这里主要介绍单例模式使用Java的实现(包括饿汉式及懒汉式)。

实现

这里使用Log类作为例子,Log对象需要在程序中只有一个对象且只初始化一次。

饿汉式

饿汉式的单例模式理解起来是比较容易的,就是在单例类加载的时候就初始化需要单例的对象。实现也比较容易。

public class Singleton{
private static Log logObj = new Log();

public static Log getInstance(){
return logObj;
}
}


懒汉式

如果logObj需要占用很大的内存,如果一开始就初始化logObj,那么会占用大量的内存。此时,有人就想,如果我在想用的时候再初始化Log类的对象,像懒汉一样,只有用到的时候再初始化,需要怎么设计呢?

实现一(非线程安全版本)

public class Singleton{
private static Log logObj = null;

public static Log getInstance(){
if(logObj == null){
logObj = new Log();
}
return logObj;
}
}


实现二(线程安全版本)

public class Singleton{
private static Log logObj = null;

public static synchronized Log getInstance(){
if(logObj == null){
logObj = new Log();
}
return logObj;
}
}


为了实现线程安全,这个版本的实现牺牲了一定的效率,如果logObj已经初始化,那么其他线程还需要同步的进入getInstance方法,会造成效率的损失。于是,有些人实现了下面的版本。

实现三(错误版本)

public class Singleton{
private static Log logObj = null;

public static Log getInstance(){
if(logObj == null){
synchronized(Singleton.class){
if(logObj == null){
logObj = new Log();
}
}
}
return logObj;
}
}


乍看起来上面的版本是没问题的,如果某个线程A发现logObj 还没初始化,那么就进入同步块初始化logObj,如果在这期间有其他线程B进入,那么线程B就会等待进入同步块,等待A 线程退出同步块,logObj 已经初始化了,B 线程进入同步块后发现logObj 不为null,退出同步块,不再初始化logObj 。 这样既实现了线程安全,又兼顾了效率,确实是很聪明的编码方式。但是问题来了,由于指令重排序的存在,会导致Log在完全初始化之前logObj就已经不为null
afeb
。这样其他线程可能会得到未完全初始化的对象。

解决方法

JDK1.5版本后扩展了volitile语义,可以保证上述代码的正确性,为此只要将logObj 声明为volitile即可(volitile之前只是保证内存的可见性而已)。

使用静态内部类。

加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。

并且jvm会保证类加载的线程安全问题,所以利用这个特性可以写出兼顾效率与保证线程安全的版本。

实现四(兼顾效率与线程安全的版本)

public class Singleton{
static class LogHolder{
static Log logObj = new Log();
}

public static Log getInstance(){
return LogHolder.logObj;
}
}


这样在Singleton类加载时,并不会加载LogHolder,也就不会初始化Log,如果有线程访问getInstance方法,那么jvm会首先加载LogHolder类,并保证初始化logObj,最后返回logObj。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: