单例模式
1、概念
-
保证类只有一个实例,并提供一个全局访问点
2、方式
-
怎么样来保证只有一个类的实例呢
-
我们可以将类的构造器私有,让用户不能在new对象,让类自身负责保存它的唯一实例,并提供一个全局访问点。
-
3、代码
-
public class Singleton01 {private static Singleton01 instance; //这个属性要变为static全局private Singleton01() { //构造方法为私有是防止用户new对象}public static Singleton01 getInstance() { //全局访问点if(instance == null) { instance = new Singleton01();}return instance;} }
4、改进(双重检查锁定)
-
上述就是单例模式的一个简单的代码了,但是我们会发现一个问题:
- 当多个线程访问时,可能会出现重复new对象的情况,导致实例不唯一
- 遇到这种情况,我们可以对代码进行加锁来约束
- 当多个线程访问时,可能会出现重复new对象的情况,导致实例不唯一
-
public class Singleton01 {private volatile static Singleton01 instance; //这个属性要变为static全局,volatile关键字是为了避免发生指令重排序private Singleton01() { //构造方法为私有是防止用户new对象}public static Singleton01 getInstance() { //全局访问点if (instance == null) { // 1synchronized(Singleton01.class) { //2、加锁防止多线程访问时,多new一个对象//当判断没有对象的时候new对象,有对象生成的时候不重新生成对象,达到单例的要求if(instance == null) { //3 instance = new Singleton01(); //4}}}return instance;} }
-
上述代码我们可以看到,我们对创建唯一实例的代码进行了加锁处理,但是同时我们也可以看到有两个判断为null 的判断语句,为什么呢?
- 当代码运行到 1 处时,判断有实例后,就会直接返回实例,大大减少了synchronized的开销。
- 当实例为 null 时,使用synchronized关键字在多线程环境中避免多次创建对象。
- 当代码运行到 3 处,再次判断是否为null,因为另一个线程可能在获取锁之前就创建成功了,所以我们还需判断一次。
-