应用场景
用来管理共享的资源,例如数据库连接或者线程池,在全局仅有一个实例。
定义
单例模式确保一个类只有一个实例,并提供一个全局访问点。
延迟实例化(懒汉式)
懒汉式,顾名思义就是比较懒,不用的时候不实例化,用到它的时候再实例化,这对资源敏感的对象特别重要。
public class LazyBoy{
private static LazyBoy instance;
private LazyBoy(){
//资源加载等
}
public static void getInstance(){
if(instance == null){
instance = new LazyBoy();
}
return instance;
}
}
我们可以发现,懒汉式单例模式拥有一个静态的单例类的变量
,一个私有的构造函数
,一个静态的获取单例对象的方法
,这基本上是懒汉式的标配。
懒汉式的问题所在
当我们使用多线程时,会发生如下所示的情况:
当进行多线程操作时,当Thread1运行到if(instance==null)
时,即将运行下一行,由于CPU的并发性,该线程时间片用完阻塞,轮到Thread2执行,并执行到if(instance==null)
,发现是true
。
此时,会有两个线程进入到实例化代码块,这样会造成两个不同的实例对象,这就是所谓的非“线程安全”。
处理多线程
1、同步的方法
第一种方法就是使用同步的方法:
public class LazyBoy{
private static LazyBoy instance;
private LazyBoy(){
//加载资源
}
public static synchronized LazyBoy getInstance(){
if(instance == null){
instance = new LazyBoy();
}
return instance;
}
}
这里使用了同步的方法(synchronized
),但是使用synchronized 会大大降低执行的性能。
2、双重检查锁
第二种是使用双重检查锁
的方法:
public class LazyBoy{
private volatile static LazyBoy instance;
private LazyBoy(){
//加载资源
}
public static LazyBoy getInstance(){
if(instance == null){
synchronized(LazyBoy.class){
if(instance == null){
instance = new LazyBoy();
}
}
}
return instance;
}
}
使用了volatile
类型修饰符,该修饰词只能让该变量尽快的变化,通知线程该变量发生变化,但不能保证原子性。
volatile类型修饰符,参考:http://www.importnew.com/24082.html
3、饿汉式
由于上面懒汉式是非线程安全的,通常情况下又会进行多线程优化。现在提出一种程序初始化时,就进行实例化的方案,称为饿汉式
单例模式。
代码模式如下:
public class DiligentBoy(){
public static DiligentBoy instance = new DeligentBoy();
private DiligentBoy(){
//资源初始化
}
public DilegentBoy getInstance(){
return instance;
}
}
在静态初始化器中创建单例。在初始化时实例化了变量,这保证了线程安全。