1.1. 单例模式
写设计模式的时候,我在思考为什么要写设计模式,正如鲁迅先生说的:世界上本来是没有路的,走得多了就有路了。设计模式也是先生所讲的一样,别人已经发明了一个轮子,后人只需要使用即可。模式就是解决某一类问题的。分享牛系列,分享牛专栏,分享牛。
单例模式:为什么需要单例模式呢?很简单单例模式本质就是控制类的实例的个数。假如我们需要一个工具类去读取文件,很显然只需要一个类就可以了,因为多个类把文件内容一次性加载到内存而且是重复的是没有意义的。这个就是典型的单例模式需要解决的问题。分享牛系列,分享牛专栏,分享牛。
扩展:典型的LRU算法,我们怎么实现呢。这个就是单例模式的延伸,控制类的实例的个数,我们可以控制类的实例的个数,不在局限于单一的一个。本质都是一样控制类的实例的个数。分享牛系列,分享牛专栏,分享牛。
下面开始单例模式代码的书写吧?
1.2. 实现方式一
很简单的一个例子,既然单例模式控制的是类的实例的个数,那我们就不让外部实例化,怎么不让外部实例化呢?我们可以把类的构造方法私有即可,在这里不考虑反射的使用。不让外部实例化,而且还要让外部的其他客户使用,那我们就提供一个static的方法,让外部使用吧。
package com.shareniu.singleton; public class Singleton1 { private Singleton1() { super(); } private static Singleton1 instance=new Singleton1(); public static Singleton1 getInstance() { return instance; } public static void main(String[] args) { Singleton1 instance1 = Singleton1.getInstance(); Singleton1 instance2 = Singleton1.getInstance(); System.out.println(instance1+"---"); System.out.println(instance2+"---"); } }
1.3. 实现方式二
package com.shareniu.singleton; public class Singleton2 { private Singleton2() { } private static Singleton2 instance; public static Singleton2 getInstance() { if (instance==null) { instance=new Singleton2(); } return instance; } public static void main(String[] args) { Singleton2 instance1 = Singleton2.getInstance(); Singleton2 instance2 = Singleton2.getInstance(); System.out.println(instance1+"---"); System.out.println(instance2+"---"); } }
if (instance==null) 第一个不存在再去获取类的实例,好像看着没问题慢但是在多线程的条件下,这种方法是有问题的?假如第一个线程执行到if (instance==null) 但是还没有实例化, 第二个线程也跟进来了,那这样不就实例化2个对象了,所以这种方式多线程有问题,而且第一次加载比较慢。
1.4. 实现方式三
在方法2的基础上,我们改造代码如下:加入synchronized 关键字,这样多线程的问题就解决了,但是每次获取实例的时候,其他线程就是等待状态,所以这一种synchronized 范围太大了。阻塞时间就长。
package com.shareniu.singleton; public class Singleton4 { private Singleton4() { } private static Singleton4 instance; public synchronized static Singleton4 getInstance() { if (instance==null) { instance=new Singleton4(); } return instance; } public static void main(String[] args) { Singleton4 instance1 = Singleton4.getInstance(); Singleton4 instance2 = Singleton4.getInstance(); System.out.println(instance1+"---"); System.out.println(instance2+"---"); } }
1.5. 实现方式四
这种方式将synchronized 的范围缩小到if (instance==null) {内部 双重锁机制。
package com.shareniu.singleton; public class Singleton3 { private Singleton3() { } private static Singleton3 instance; public static Singleton3 getInstance() { if (instance==null) { synchronized (Singleton3.class) { instance=new Singleton3(); } } return instance; } public static void main(String[] args) { Singleton3 instance1 = Singleton3.getInstance(); Singleton3 instance2 = Singleton3.getInstance(); System.out.println(instance1+"---"); System.out.println(instance2+"---"); } }
1.6. 实现方式五
这种方式利用类的加载机制,静态块代码只加载一次,同时也避免了多线程的问题。建议使用这种方式。
package com.shareniu.singleton; public class Singleton5 { private Singleton5() { } private static class LazyHolder { private static final Singleton5 INSTANCE = new Singleton5(); } public synchronized static Singleton5 getInstance() { return LazyHolder.INSTANCE; } public static void main(String[] args) { Singleton5 instance1 = Singleton5.getInstance(); Singleton5 instance2 = Singleton5.getInstance(); System.out.println(instance1 + "---"); System.out.println(instance2 + "---"); } }
1.7. 总结
1.饿汉模式,不存在线程安全的问题。缺点就是可能一开始就创建了一个类的实例,比较占用内存。
2.懒汉式就是程序需要的时候再去加载,体现了延迟的思想、缺点:第一次可能慢,以后就差不多了。差不多是查多少呢,还是需要去判断在取值吧。
3.利用类的加载机制,推荐使用,类的加载机制第一不需要判断,第二解决了线程的安全问题。
分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) java架构师交流群 523988350