创建型模式:单例模式

来源:互联网 发布:淘宝店哪家符咒是真的 编辑:程序博客网 时间:2024/06/10 09:22
经过很多大神的总结,目前Java中一共23种经典的设计模式


按照目的,设计模式可以分为以下三种用途:

1.创建型模式:用来处理对象的创建过程

2.结构型模式:用来处理类或者对象的组合

3.行为型模式:用来对类或对象怎样交互和怎样分配职责进行描述


创建型模式用来处理对象的创建过程,主要包含以下5种设计模式:
 工厂方法模式(Factory Method Pattern)
 抽象工厂模式(Abstract Factory Pattern)
 建造者模式(Builder Pattern)
 原型模式(Prototype Pattern)
 单例模式(Singleton Pattern)


结构型模式用来处理类或者对象的组合,主要包含以下7种设计模式:
 适配器模式(Adapter Pattern)
 桥接模式(Bridge Pattern)
 组合模式(Composite Pattern)
 装饰者模式(Decorator Pattern)
 外观模式(Facade Pattern)
 享元模式(Flyweight Pattern)
 代理模式(Proxy Pattern)


行为型模式用来对类或对象怎样交互和怎样分配职责进行描述,主要包含以下11种设计模式:
 责任链模式(Chain of Responsibility Pattern)
 命令模式(Command Pattern)
 解释器模式(Interpreter Pattern)
 迭代器模式(Iterator Pattern)
 中介者模式(Mediator Pattern)
 备忘录模式(Memento Pattern)
 观察者模式(Observer Pattern)
 状态模式(State Pattern)
 策略模式(Strategy Pattern)
 模板方法模式(Template Method Pattern)
 访问者模式(Visitor Pattern) 


本节主要讲解单例模式(Singleton Pattern)又叫单态模式


单例模式有以下特点:

  1、单例类只能有一个实例。

  2、单例类必须自己创建自己的唯一实例。

  3、单例类必须给所有其他对象提供这一实例。

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。


单例模式的使用场景:

在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具备资源管理器的功能。

例如:

每台计算机可以有若干个打印机,但只能有一个Printer Spooler【打印机要工作正常,您需要确保机器上的Printer Spooler 这个服务处于运行状态。如果您的Printer Spooler服务没有开启,可以在服务中手动开启Printer Spooler】,以避免两个打印作业同时输出到同一个打印机中。


方法一:饿汉模式

1.饿汉模式单例(线程安全)

public class Singleton1 {

//将自身的实例对象设置为一个属性,并加上static和final修饰符,这样就能保证内存中只有一个对象

private static final Singleton1 instance = new Singleton1();

//把构造方法设置为私有化,这样其他类就不能通过构造方法创建实例了

private Singleton1() {

}

//通过这个静态方法向外界提供这个类的实例,而且这个实例在一开始就已经创建了

public static Singleton1 getInstance() {

return instance;

}

}

这种方式是基于classloder机制避免了多线程的同步问题。instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。


2.饿汉变种单例(线程安全)

public class Singleton1 {
private Singleton1 instance = null;
static {
instance = new Singleton1 ();
}
private Singleton1 (){

}
public static Singleton1 getInstance() {
return this.instance;
}
}

实际上和饿汉模式是一样的!只是看起来不太一样!


方法二:懒汉模式

1.真正的懒汉模式(线程不安全)

public class Singleton2 {

//在第一次调用的时候实例化自己 

private static Singleton2 instance2 = null;

private Singleton() {} //避免被外部访问

//静态工厂方法,要加上同步

public static Singleton2 getInstance() {

if (instance2 == null)

instance2 = new Singleton2();

return instance2;

}

}


2.懒汉模式(解决线程安全问题)

public class Singleton2 {

//在第一次调用的时候实例化自己 

private static Singleton2 instance2 = null;

private Singleton() {} //避免被外部访问

//静态工厂方法,要加上同步

public staticsynchronized Singleton2 getInstance() {

if (instance2 == null)

instance2 = new Singleton2();

return instance2;

}

}


3.懒汉模式中的双重检查锁定(线程安全)

public class Singleton2 {

//在第一次调用的时候实例化自己 

private static Singleton2 instance2 = null;

//避免被外部访问

private Singleton() {

//静态工厂方法,要加上同步

public static Singleton2 getInstance() {

if (instance2 == null) {

        synchronized(Singleton2 .class) {

if (instance2 == null) {

instance2 = new Singleton2 ();

}

}

}

return instance2;

}

}


4.懒汉模式中的静态内部类

public class Singleton2 {    

private static class LazyHolder {    

private static final Singleton2 INSTANCE = new Singleton2 ();    

}    

private Singleton2 (){

}    

public static final Singleton2 getInstance() {    

return LazyHolder.INSTANCE;    

}    

}

第4种比上面2、3都好一些,既实现了线程安全,又避免了同步带来的性能影响。

这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,

它跟饿汉模式不同的是(很细微的差别):

饿汉模式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),

而这种方式是Singleton类被装载了,instance不一定被初始化。因为LazyHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载LazyHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton2 类加载时就实例化,因为我不能确保Singleton2 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比饿汉模式就显得很合理。


上面三种懒汉模式的区别:

第2种:在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,性能损耗太大。

第3种:在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗。

第4种:利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。


方法三:枚举

public enum Singleton {  

INSTANCE;

public void whateverMethod() {  

}

}  
这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊。


饿汉模式和懒汉模式区别

1.初始化上:

饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

2、线程安全:

从代码上不难看出饿汉式是线程安全的,可以直接用于多线程而不会出现问题。

懒汉模式(第一种)本身是非线程安全的,正常使用的时候应该加上线程安全的结局方案,

上面(2、3、4)三种方案可以考虑一下,只是上面三种实现在资源加载和性能方面稍微有些区别。

3、资源加载和性能:

饿汉模式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,

在第一次调用时速度也会更快,因为其资源已经初始化完成。

懒汉模式则会延迟加载,第一次使用的时候才会实例化对象,第一次调用时要做初始化,性能上会有些延迟,之后就和饿汉式一样了。


总结:

下面有两个问题需要注意:

1、如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。

假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。

2、如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。

不管怎样,如果你序列化一个单例类的对象,接下来复原多个单例类对象,那你就会有多个单例类的实例。

对问题1修复的办法是:

private static Class getClass(String classname) throws ClassNotFoundException {     

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();     

if(classLoader == null)

classLoader = Singleton.class.getClassLoader();     

return (classLoader.loadClass(classname));

     }     

 }  
 
 
对问题2修复的办法是:
public class Singleton implements java.io.Serializable {     

public static Singleton INSTANCE = new Singleton();

protected Singleton() {      

}     

private Object readResolve() {     

return INSTANCE;     

}    

 }   


 

最后,如果大家有什么不同的意见或者更好的看法欢迎提出来!



0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 子与母床上大战小说 穷山村的娘和儿子全文阅读 乱辈真实故事福林 为什么我没有朋友 妈毛又多又黑 脱掉母亲的白衬衫 漫画 母亲臀部抬起配合我 跟儿子洗澡没忍住做了一次 偷摸熟睡的母H 母亲只好认命抬起右腿 晚上故意到儿子房间睡 小说母亲的短裤衩 抱着母亲的腰疯狂地耸小说 姑妈穿裙子坐在我的腿的故事上 儿子18妈 妈38片国中 顶开高贵母亲的两辨 第章母亲抬腰迎合 母亲开始抗拒慢慢变迎合我口诉 母亲开始抗拒慢慢迎合我 第章顶开高贵母亲两瓣 母亲从抗拒慢慢变的迎合我 母亲疯狂迎合我txt小说下载 来吧儿子妈要你 影视片 儿子进来今晚妈让你做个够电影 儿子今晚妈让你做人个够知音网 离婚后和儿子睡觉 儿子让妈给你留个种吧 儿子妈要你的大吉吧 没开灯把小偷当老公 女儿怀孕我给姑爷解决问题 有给儿子口的吗 儿子你的真大 儿子想怎么弄就怎么弄吧 坏儿子还能在深点吗 睡着儿子顶了进来 儿子别急妈是你的小说 儿子今晚妈让你做人个够的视频 母亲和我做完跟父亲做在线阅读 高粱地里野坑头 明明有老公却想跟儿子 玉米地理日刘审一