Java设计模式之单例模式 超详细

版权声明:转载请注明出处!! https://blog.csdn.net/IPI715718/article/details/88387532

一,概念:单例模式就是类在内存中当且仅当一个实例对象,而且不允许外界实例化该类的对象,对象的创建必须在类内部,提供一个静态方法返回唯一实例对象的引用。

二,单例模式的思路:

  1. 将构造方法设计成private私有的。
  2. 实例对象在内部创建。
  3. 提供一个类方法返回实例对象的引用。

 三,单例模式的八种写法:

1,饿汉式(静态常量 可以用不推荐)

public class Singleton {
    private static Singleton singleton = new Singleton();
	
	//将构造器设置成private,不允许其他类创建该类的对象
	private Singleton(){
		
	}
	
	public static Singleton getInstance(){
		return singleton;
	}

}

2,饿汉式(静态块 可以用不推荐)

public class Singleton {
	private static Singleton singleton = null;
	
	//将构造器设置成private,不允许其他类创建该类的对象
	private Singleton(){
		
	}
	
	static{
		singleton = new Singleton();
	}
	
	public static Singleton getInstance(){
		return singleton;
	}
}

 以上两种饿汉式的写法可以使用,原理都是在类初始化时创建实例对象,但是存在着缺点,考虑实例对象从不使用的情况,但是类在加载时已经创建了对象,会造成内存的浪费。

3,懒汉式(线程不安全写法 多线程不可用)

public class Singleton {
    private static Singleton singleton = null;
	
	private Singleton(){
		
	}
	
	public static Singleton getInstance(){
		if(singleton==null){
			singleton = new Singleton();
		}
		return singleton;
    }
}

这种写法虽然实现了lazy-init,但是存在着线程安全问题,如果在 if 判断为 true ,A线程进入if代码块,A线程还未来得及创建对象,B线程也通过了判断进入if代码块,就会创建两个或者多个对象,就不是单例模式了,因此多线程下不可用。

4,懒汉式(线程安全写法 synchronized  效率不感人)

public class Singleton {
	private static Singleton singleton = null;
	
	private Singleton(){
		
	}
	
	public static Singleton getInstance(){
		synchronized (Singleton.class) {
			if(singleton==null){
				singleton = new Singleton();
			}
			return singleton;
		}
	}
}

这种写法实现了lazy-init,并且使用了同步代码块实现了线程的安全,但是“效率感人”,第一次调用getInstance对象不存在不必多说效率只能进入代码块创建对象这种耗时是理所当然的,但是当二次调用时就会效率低下,例如有A,B两个线程,A进入代码块进行判断,拿到了类锁,B也需要判断,但是锁被A占用着,必须等待着A释放所资源处于阻塞状态,但其实对象已经创建,只需要判断返回即可,就是判断在同步块里大大浪费了时间,因此这种写法大大降低了效率。

5,懒汉式(改进写法 synchronized 线程不安全)

public class Singleton {
	private static Singleton singleton = null;
	
	private Singleton(){
		
	}
	
	public static Singleton getInstance(){
		if(singleton==null){
			synchronized (Singleton.class) {
				singleton = new Singleton();
			}
		}
		
		return singleton;
	}
}

这种写法貌似可以提高效率,因为当第二次调用getInstance方法时可以在同步块之前进行判断,多个线程之间可以同时进行判断,无需等待,瞬间感觉神清气爽,我们来考虑一下第一次调用getInstance方法时会不会存在线程安全问题????,答案是确定的,会存在,A,B两个线程,A线程判断后true即将进入同步块这时还未创建对象,B线程此时通过了判断,在同步块前阻塞,等A线程创建对象后,A线程释放掉锁资源,B线程拿到锁进入代码块,又一次创建了对象,造成了安全问题,因此多线程下不能使用。

6,双重检查式(懒汉式究极进化体)

public class Singleton {
	private static Singleton singleton = null;
	
	private Singleton(){
		
	}
	public static Singleton getInstance(){
		if(singleton==null){
			synchronized (Singleton.class) {
				if(singleton==null){
					singleton = new Singleton();
				}
			}
		}
		return singleton;
	}
}

 双重检查其实也是懒汉式的改进型(仔细体会,实在是高),两次判断单例是否已经被创建。

  1. 外层判断为了提高效率。
  2. 内层判断为了线程安全。

解释一下:

先说内层判断主要是为了多线程下因为外层判断时是线程不安全的,导致我上面第五种写法写的那种线程安全问题产生的原因,这里加个判断就可以防止对象的二次创建,破坏了单例模式。

再说外层判断,外层判断是为了在单例对象已经存在时多个线程进入同步块抢夺类锁时的阻塞,多个线程可以同时进行外层判断false意味着对象存在,就不需要进入同步代码块挣个你死我活。 

7,静态内部类

 public class Singleton {
	 private Singleton(){
		 
	 }
	 
	 private static class SingletonInstance{
		 private static Singleton singleton = new Singleton();
	 }
	 
	 public static Singleton getInstance(){
		 return SingletonInstance.singleton;
	 }
}

这种写法是利用内部静态类,当首次获取是会实例化对象时会去加载并且实例化内部类,这时会去创建内部静态常量(也是外部类的实例化对象),也能做到懒加载。

8.枚举式

public enum Singleton {
    INSTANCE;
    public void whateverMethod() {

    }
}

 枚举我不太懂,后期再去learn。

猜你喜欢

转载自blog.csdn.net/IPI715718/article/details/88387532