单例设计模式

x33g5p2x  于2021-11-15 转载在 其他  
字(2.2k)|赞(0)|评价(0)|浏览(214)

使用场景

单例模式的作用是 保证一个类在JVM中只能存在一个对象,防止多余的对象浪费JVM的内存

创建方式

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 Singleton(){
	}
	
	public static Singleton getInstance(){
		if(singleton == null){
			singleton = new Singleton();
		}
		return singleton;
	}
}

上述的代码在多线程的情况下,会出现错误,当两个线程同时进入singleton = null 的时候,会同时创建两个Singleton对象,返回两个不同的实例。

最简单粗暴的方式就是加锁

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

上述的代码可以达到线程安全,但是性能太低,因为当singleton不为null的时候,获取也需要加锁

//错误的例子
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;
	}
}

上述的代码是错误的,因为当两个线程同时抢锁来创建实例的时候,第一个线程创建完后,第二个在进入临界区的时候,又会重新创建一次实例,导致实例被创建了两次,返回了两个不同的实例。

所以,要在临界区代码中加入一个双重判空。

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;
	}
}

但是上述的代码还是会存在问题,因为JVM会对singleton = new Singleton()进行指令优化。

singleton = new Singleton();这个指令并不是原子操作,JVM会对其进行指令重排

singleton = new Singleton(); 正常的执行流程为

1、 allocate memory //为对象分配内存
2、 initiate(memory) //初始化对象
3、singleton = memory //将对象赋予给引用

指令重排后的执行流程
1、 allocate memory //为对象分配内存
2、 singleton = memory //将未初始化的对象赋予给引用
3、initiate(memory) //初始化对象

并且当执行到第2步的时候,也就是将未初始化的对象赋值给引用后,如果这时其他线程调用了getInstance(),此时singleton != null,所以会得到一个未初始化的对象,当线程调用未初始化的对象的时候,就会出错。

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

用volatile修饰 singleton,JVM不会对volatile修饰的对象进行指令重排。

3、静态内部类

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

相关文章

微信公众号

最新文章

更多