单列模式使用的注意事项
只有一个私有的构造函数,包含该类自身的静态私有对象,提供一个静态的公有的函数用于创建或获取它本身的静态私有对象。
单列模式的误区
对如此简单的一个模式,我曾经陷入了一个误区,就是无论在什么时候,只要涉及到使用某个不需要重复实例化的类的时候我都使用单列模式,然而这里有个更好的实现方法,那就是工具类(静态方法)即可完美解决这个问题。
单列模式的使用场景
那么单列模式到底该用在何处呢?虽然我个人还没有遇到适合使用该方法的地方,但是在网上粗略查了一下,一般是用于系统的统计以及需要共用一些数据的时候。这样一个类中只有一个实例才会显的有意义。
比如:任务管理器,回收站,网站计数器,日志应用,数据库连接池,多线程连接池,HttpApplication等。
单列模式经典例子
单列模式实例创建有两种,懒汉式与饿汉式。
饿汉模式,即刚开始启动时就把所有实例创建好,这样会降低启动速度,并且系统会占用大量资源。
public class HungrySingleton { private static HungrySingleton instance = new HungrySingleton(); private HungrySingleton() { } public static HungrySingleton getInstance() { return instance; } }
懒汉式即懒启动模式,只有该实例被需要的时候创建,这样可以减少启动时所需资源,加快启动速度。一般情况下我们都想延时加载对象,希望在第一次获取的时候才构造对象。
public class LazySingleton { private static LazySingleton instance = null; private LazySingleton() { } public static LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
懒汉式如果在多线程的情况下,还需要使用线程锁以确保只有一个实例被创建。
public static class Singleton{ private static Singleton instance=null; private Singleton(){ //do something } public static Singleton getInstance(){ if(instance==null){ synchronized(Singleton.class){ if(null==instance){ instance=new Singleton(); } } } return instance; } }
这里不给方法加线程锁是因为每个线程调用getInstance都要加锁,我们想要只在第一次调用getInstance时加锁。
多线程中还会遇到一个指令重排序问题(见注释1)如果instance实例变量用volatile修饰就可以解决该问题,volatile修饰的话就可以确保instance = new Singleton();对应的指令不会重排序
public class Singleton { private static volatile Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if(instance == null) { synchronzied(Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; } }
注释1
在线程A中有两条语句对这两个共享变量进行赋值操作:
a = 1;
b = 2;
假设当线程A对a进行复制操作的时候发现这个变量在主内存已经被其它的线程加了访问锁,那么此时线程A怎么办?等待释放锁?不,等待太浪费时间了,它会去尝试进行b的赋值操作,b这时候没被人占用,因此就会先为b赋值,再去为a赋值,那么执行的顺序就变成了:
b = 2;
a = 1;