单件模式
定义:确保一个类只有一个实例,并提供全局访问点。
编写格式:
1 2 3 4 5 6 |
|
有一些对象其实我们只需要一个,比如线程池、缓存、对话框、处理偏好设置和注册表的对象、日志对象。如果制造出多个实例,就会导致程序的行为异常、资源使用过量,或者不一致的结果等。
单件模式实现1:延迟实例化
懒汉式
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
类图:
“延迟实例化”在我们不需要这个实例的时候它将不会产生。
上面的方法的确实现了单件模式,但是在某些情况下将出现一些严重的问题,例如:
在多线程的情况下,可能会创建多个对象。怎么解决多线程单件问题呢?下面有三种方法:
单件模式实现2:通过synchronized关键字解决多线程问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
同步(synchronized)可以解决多线程存在的问题,但是同步会降低性能,且第一次执行此方法时才真正需要同步,一旦设置好uniqueInstance变量,就不再需要同步这个方法了,之后每次调用这个方法,同步都是一种累赘。
建议:
1.如果getInstance()d的性能对应用程序不是很关键,则可以忽略同步带来的性能下降问题。
2.同步一个方法可能会让程序执行效率下降100倍,如果getInstance()是程序使用在频繁运行的地方则不建议这样使用。
3.如果应用程序总是创建并使用单件实例或创建运行时的负担不太繁重,可以使用“急切实例化”创建实例,而不用“延迟实例化”的做法。
单件模式实现3:急切实例化
饿汉式
1 2 3 4 5 6 7 8 |
|
利用这个做法,我们依赖JVM在加载这个类时马上创建此唯一的单件实例。JVM保证在任何线程访问uniqueInstance静态变量之前,一定先创建此实例。
单件模式实现4:双重检查加锁
使用双重检查加锁(double-checked locking),首先检查实例是否已经创建了,如果尚未创建,才进行同步。这样一来,只有第一次会同步。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
其他问题:
在Java1.2之前,垃圾收集器有个bug,会造成当单件在没有全局的引用时被当作垃级清除。
多个类加载器(class loader)可能会导致单件失效。
单件模式要点:
- 单件模式确保程序中一个类最多只有一个实例。
- 单件模式也提供访问这个实例的全局点。
- 在Java中实现单件模式需要私有的构造器、一个静态方法和一个静态变量。
- 确定在性能和资源上的限制,然后小心地选择适当的方案来实现单件,以解决多线程的问题(我们必须认定所有的程序都是多线程的)。
- 如果不是采用第五版的Java 2,双重检查加锁实现会失效。
- 小心,你如果使用多个类加载器,可能导致单件失效而产生多个实例。
- 如果使用JVM1.2或之前的版本,你必须建立单件注册表,以免垃圾收集器将单件回收。