经典的单例模式: public class Singleton private static Singleton uniqueInstance = null;
private Singleton() // Exists only to defeat instantiation.
public static Singleton getInstance() if (uniqueInstance == null) uniqueInstance = new Singleton(); return uniqueInstance; Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内, Singleton的唯一实例只能通过getInstance()方法访问(事实上,通过Java反射机制是能够 实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效), 该方法没有考虑线程安全,在并发环境下很可能出现多个Singleton实例。
饿汉式单例类: //饿汉式单例类.在类初始化时,已经自行实例化 public class Singleton1 private Singleton1() //私有的默认构造器 //已经自行实例化 private static final Singleton1 single = new Singleton1(); //静态工厂方法 public static Singleton1 getInstance() return single;
懒汉式单例类:
//懒汉式单例类.在第一次调用的时候实例化 public class Singleton2 private Singleton2() //私有的默认构造器 private static Singleton2 single=null; //注意,这里没有final //静态工厂方法 public synchronized static Singleton2 getInstance() if (single == null) single = new Singleton2(); return single; 登记式单例类
import java.util.HashMap; import java.util.Map;
//类似Spring里面的方法,将类名注册,下次从里面直接获取。 public class Singleton3 private static Map map = new HashMap(); static Singleton3 single = new Singleton3(); map.put(single.getClass().getName(), single); protected Singleton3() //保护的默认构造子 //静态工厂方法,返还此类惟一的实例 public static Singleton3 getInstance(String name) if(name == null) name = Singleton3.class.getName(); System.out.println("name == null"+"--->name="+name); if(map.get(name) == null) try map.put(name, (Singleton3) Class.forName(name).newInstance()); catch (InstantiationException e) e.printStackTrace(); catch (IllegalAccessException e) e.printStackTrace(); catch (ClassNotFoundException e) e.printStackTrace(); return map.get(name); 所谓单例模式,简单来说,就是在整个应用中保证只有一个类的实例存在。就像是Java Web中的application, 也就是提供了一个全局变量,用处相当广泛,比如保存全局数据,实现全局性的操作等。 1. 最简单的实现 首先,能够想到的最简单的实现是,把类的构造函数写成private的,从而保证别的类不能实例化此类, 然后在类中提供一个静态的实例并能够返回给使用者。这样,使用者就可以通过这个引用使用到这个类的实例了。 public class SingletonClass
private static final SingletonClass instance = new SingletonClass(); public static SingletonClass getInstance() return instance; private SingletonClass() 如上例,外部使用者如果需要使用SingletonClass的实例,只能通过getInstance()方法,并且它的构造方法是private的,这样就保证了只能有一个对象存在。 2. 性能优化——lazy loaded 上面的代码虽然简单,但是有一个问题——无论这个类是否被使用,都会创建一个instance对象。如果这个创建过程 很耗时,比如需要连接10000次数据库,并且这个类还并不一定会被使用,那么这个创建过程就是无用的。怎么办呢? 为了解决这个问题,我们想到了新的解决方案: public class SingletonClass
private static SingletonClass instance = null; public synchronized static SingletonClass getInstance() if(instance == null) instance = new SingletonClass(); return instance; private SingletonClass() 是要getInstance()加上同步锁,一个线程必须等待另外一个线程创建完成后才能使用这个方法,这就保证了单例的唯一性。 4. 又是性能 上面的代码又是很清楚很简单的,然而,简单的东西往往不够理想。这段代码毫无疑问存在性能的问题—— synchronized修饰的同步块可是要比一般的代码段慢上几倍的!如果存在很多次getInstance()的调用, 那性能问题就不得不考虑了! 让我们来分析一下,究竟是整个方法都必须加锁,还是仅仅其中某一句加锁就足够了?我们为什么要加锁呢? 分析一下出现lazy loaded的那种情形的原因。 原因就是检测null的操作和创建对象的操作分离了。如果这两个操作能够原子地进行,那么单例就已经保证了。 于是,我们开始修改代码: public class SingletonClass
private static SingletonClass instance = null; public static SingletonClass getInstance() synchronized (SingletonClass.class) if(instance == null) instance = new SingletonClass(); return instance; private SingletonClass() 首先去掉getInstance()的同步操作,然后把同步锁加载if语句上。但是这样的修改起不到任何作用: 因为每次调用getInstance()的时候必然要同步,性能问题还是存在。如果……如果我们事先判断一下是不是为null再去同步呢? public class SingletonClass
private static SingletonClass instance = null;
public static SingletonClass getInstance() if (instance == null) synchronized (SingletonClass.class) if (instance == null) instance = new SingletonClass(); return instance;
public static SingletonClass getInstance() if (instance == null) synchronized (SingletonClass.class) if(instance == null) instance = new SingletonClass(); return instance;
private SingletonClass()
然而,这只是JDK1.5之后的Java的解决方案,那之前版本呢?其实,还有另外的一种解决方案, 并不会受到Java版本的影响: public class SingletonClass private static class SingletonClassInstance private static final SingletonClass instance = new SingletonClass();
public static SingletonClass getInstance() return SingletonClassInstance.instance;