一、单例模式是为了控制类只有一个实例对象,通常在以下两种情况需要考虑使用单例模式:
1、业务逻辑的需求
2、性能的考虑
二、单例模式的机构图:
三、单例模式的几种实现形式:
1、形式一
public class Singleton2 {
private static Singleton2 instance;
private Singleton2() {
}
public static Singleton2 getInstance(){
if(instance==null){
instance = new Singleton2();
}
return instance;
}
}
2、形式二
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance(){
return instance;
}
}
注意:对于第一种形式来说,其最大的问题是不适合于多线程模式。
3、形式三——改进形式一,使其在多线程模式下能正常运行:
public class Singleton2 {
private static Singleton2 instance;
private Singleton2() {
}
public synchronized static Singleton2 getInstance(){
if(instance==null){
instance = new Singleton2();
}
return instance;
}
}
加上关键字synchronized后,确实能保证线程的安全,但是存在的问题是,验证影响效率,因为同步整个方法,会使得所有的线程需要顺序执行,不能并发执行。
4、形式四——修正形式三,提高效率:
public class Singleton2 {
private volatile static Singleton2 instance;
private Singleton2() {
}
public static Singleton2 getInstance(){
if(instance==null){
synchronized(Singleton2.class){
if(instance==null){
instance = new Singleton2();
}
}
}
return instance;
}
}
这段代码的核心是:双重检查并加锁。双重检查的作用在于不需要同步整个方法,可以并发执行程序,并且在加锁的情况下可以保证线程的安全。这个地方还需要注意的关键字是volatile,但是该关键字仅仅能保证读取的是最新指值,而无法保证原子性操作,更不能保证同步。因为在JVM中每个线程有自己的内存空间,正常情况下,该内存区间会保存堆中变量的一个副本,然后完成操作后写回堆中,使用volatile关键能保证不使用线程的缓存,而直接读取堆中的值。从而保证每次读取的值是最新的值,但是有可能同时两个线程读取了该变量,然后再依次修改该变量。