单例模式:
确保一个类只有一个实例,并提供一个全局访问点。
将类设计成自己管理的一个单独的实例,同时也避免其他类再自行产生实例。想要取得单例实例,通过单例类是唯一的途径。
提供实例的全局访问点:当需要实例时,向类查询,他会返回单个实例。
public class Singleton{
private static Singleton uniqueInstance;
//其他有用实例化变量
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance==null){
uniqueInstance=new Singleton();
}
return uniqueInstance;
}
//其他方法
}
注意:多线程会破坏上述经典的单例实现。
处理多线程
(1)将getInstance()变成同步(synchronized)方法(简单直接但是性能低,对性能无要求可用此方法)
public class Singleton{
private static Singleton uniqueInstance;
//其他有用实例化变量
private Singleton(){}
/*通过增加synchronized关键字到getInstance()方法中,
我们迫使每个线程在进入这个方法之前,要先等候别的线程离开该方法。
也就是说,不会有两个线程可以同时进入这个方法*/
public static synchronized Singleton getInstance(){
if(uniqueInstance==null){
uniqueInstance=new Singleton();
}
return uniqueInstance;
}
//其他方法
}
(2)提前创建实例,而不用延迟实例化的做法(可行)
public class Singleton{
//在静态初始化器中(static initialize)中创建单件。这段代码保证了线程安全。
private static Singleton uniqueInstance=new Singleton();
//其他有用实例化变量
private Singleton(){}
public static Singleton getInstance(){
return uniqueInstance; //已有实例,直接使用
}
//其他方法
}
(3)用“双重检查加锁”,在getInstance()中减少使用同步(关注性能可用此法)
利用双重检查加锁(double-checked locking),首先检查是否实例已经创建了,如果未创建,“才”进行同步。如此,只有第一次才会同步。
public class Singleton{
//volatile关键字确保:当uniqueInstance变量被初始化成Singleton实例时,多个线程正确的处理uniqueInstance变量
private volatile static Singleton uniqueInstance;
//其他有用实例化变量
private Singleton(){}
public static Singleton getInstance(){
//检查实例,如果不存在,就进入同步区域。只有第一次才彻底执行这些代码。
if(uniqueInstance==null){
synchronized (Singleton.class){
//再次检查,如果仍是null才创建实例
if(uniqueInstance==null){
uniqueInstance=new Singleton();
}
}
return uniqueInstance;
}
//其他方法
}
要点:
(1)单例模式确保程序中一个类最多只有一个实例
(2)单例模式提供全局访问点
(3)在Java中实现单例模式需要私有的构造器、一个静态方法和一个静态变量
(4)考虑性能和资源的限制,选择合适的方案实现单例,以解决多线程的问题(必须认定所有的程序都是多线程的)
(5)若使用多个类加载器可能会导致单例失效而产生多个实例
(6)如果使用JVM1.2或之前的版本,必须建立单例注册表,以免垃圾收集器将单例回收
(7)在1.4以及更早的Java版本中许多JVM对volatile关键字的实现会导致双重检查加锁的失效