直想写点关于设计模式的东西,却懒到现在都没写过什么,今天上午看到项目中的代码,就在这个中午抽出时间写点东西,抛个项目截图先:
单例模式:
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
下面介绍Java中常用的单例模式:
一、懒汉模式
这种写法看上去似乎实现了单例模式,然鹅、实际应用中却不实用,尽管我所在的项目中有着类似的代码,我也不得不说确实如此;
优点:实现了按需创建,即懒加载
缺点:线程不安全。在多线程案例中,这种方式并没有完全实现单例模式。例如当多个线程访问getSingleton方法时,一起判断null == singleton,这时就会创建多个Singleton的实例。
实现了线程安全的懒汉模式(两种方式写法不同,却是一致的):
上面两种写法虽有差异,但运行效果却是相同的;
优点:解决了多线程的安全问题
缺点:效率却低的不要不要的。因为每当有线程访问时,就会出现线程排队等候,真正去创建实例的线程只有一个。
现实中也有人通过添加volatile关键字对对象实例进行限制,volatile保证其对所有线程的可见性,并且禁止对其进行指令重排序优化。
下面介绍下兼顾线程安全与效率的懒汉模式:
这种方式被称为双重检查锁/双重校验锁方式,通过两次判null操作,它解决了线程安全的问题,不会创建多个实例。同时、也提高了线程访问的效率问题。这种方式可以算作是懒汉模式的最优解,但并非单例模式的最优解。
二、饿汉模式
优点:写法简单,一目了然
缺点:无法做到按需创建,即懒加载,增加负载。
注意:这里的增加负载要看具体情况,就上面的代码而言,new一个Singleton实例,算不上增加负载,但如果你new的是一个很大的对象,这时你应该考虑这个问题了。
三、静态内部类
把Singleton实例放到一个静态内部类中,这样就避免了静态实例在Singleton类加载的时候就创建对象,并且由于静态内部类只会被加载一次,所以这种写法也是线程安全的。
优点:线程安全,按需创建。
缺点:1、需要额外的工作(Serializable、transient、readResolve())来实现序列化,否则每次反序列化一个序列化的对象实例时都会创建一个新的实例。
2、可能会有人使用反射强行调用我们的私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实例的时候抛异常)。
四、枚举
使用枚举除了线程安全和防止反射强行调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。因此,Effective Java推荐尽可能地使用枚举来实现单例。
最后,不管采取何种方案,请时刻牢记单例的三大要点:
- 线程安全
- 延迟加载
- 序列化与反序列化安全