单例模式
总结下单例模式的基本框架:
- 私有静态的类内部声明(private static )
私有的声明实现了类的单一声明,只能在类的内部声明这个类,在类的外部不能声明。
- 私有构造方法(private)
私有构造实现了类的单一实例化(new),只能在本类里面实例化,避免外部的实例化。
- 公共静态访问方法(public static)
这是单例类的唯一外部访问方法,只有这个方法才能获取该类的信息。
饿汉模式
饿汉因为饿所以行动非常急切,这个形象的说法对应在单例模式中就是,类在声明的时候就直接实例化。对应到代码上,就是 (变量类型 变量名=new 变量类型)。
class SingleMode{
private static SingleMode singleMode=new SingleMode();
private SingleMode(){};
public static SingleMode getItem(){
return singleMode;
}
}
懒汉模式-单线程版
class SingleMode2{
private static SingleMode2 singleMode2=null;
private SingleMode2(){};
public static SingleMode2 getItem(){
if(singleMode2==null){
singleMode2=new SingleMode2();
}
return singleMode2;
}
}
懒汉模式-多线程版
多线程版就比单线程多了一个synchronized同步锁(互斥锁),他保证了每次只有一个线程可以访问这个类。用一个比较好理解的例子来说明:
- 有很多个男生喜欢校花(多个线程)
- 他们知道了校花还没有男友(singleMode3==null)
- 于是他们都争着去追求校花,不过校花是有原则的,只能竞争机会(竞争同步锁,竞争到同步锁的可以执行)(synchronized)
class SingleMode3{
private static SingleMode3 singleMode3 =null;
private SingleMode3(){};
public synchronized static SingleMode3 getItem(){
if(singleMode3 ==null){
singleMode3 =new SingleMode3();
}
return singleMode3;
}
}
懒汉模式-多线程版进阶
进阶版增加了两个关键的点
- volatile确保对象可见性
- 双 if() 确保线程单一准确访问
继续用上面一个例子来说明:
- 有很多个男生喜欢校花(多个线程)
- 他们知道了校花还没有男友(外层 if(singleMode4=null)判断singleMode4是否实例化)
- 他们一部分人已经可以接触校花(执行getItem())
- 不过校花是有原则的,这一部分人只能竞争单一机会(竞争同步锁,线程竞争到同步锁的可以执行代码)(synchronized)
- 这时A竞争到了单一机会,通过接触了解校花确实还没有男朋友(内层 if(singleMode4=null)判断是否实例化)
- 接着发展顺利就和校花初成了(new singleMode4() )
- 可以接触校花的那一部分人在A离开后进一步判断(内层 if(singleMode4==null)判断是否实例化)
- 这时他们知道校花已经和A处成了,就只能愤愤地离开(内层判断返回false)
- 没办法接触校花的人得知传闻也打消了念头(外层判断返回flase)
外层 if 是反应比较慢还没有执行getItem方法的线程判断是否实例化
内层 if 是已经执行getItem方法的、已经在竞争同步锁的线程判断是否实例化
双层 if 判断确保了整个项目只会实例化一个对象。
class SingleMode4{
private volatile static SingleMode4 singleMode4 =null;
private SingleMode4(){};
public static SingleMode4 getItem(){
if(singleMode4 ==null){
synchronized(singleMode4){
if(singleMode4==null){
singleMode4 =new SingleMode4();
}
}
}
return singleMode4;
}
}
这对于日志记录、驱动程序对象、缓存、线程池、数据库连接等特别有用,在这些情况下,拥有多个实例可能会导致不一致或效率低下。