设计模式之单例模式

什么是单例模式
在某种情况下,我们只需要某一个类只拥有一个对象,这时候我们就应该考虑单例模式。

单例模式的特点
1.单例类只能有一个实例。

2.单例类必须创建自己的唯一的实例。

3.单例类必须向其他对象提供这一实例。

单例模式的实现
饿汉式(线程安全)
每次获得该类的对象都会创建一个新的实例,并且在类加载阶段就会创建好实例对象,因此把它称为饿汉式单例。

它避免了线程同步问题,但是由于该对象没有用也会被创建,因此会浪费内存。

在类的生命周期内类只会加载一次,而由static修饰的成员变量只有在类被加载的时候才会执行,所以也会只执行一次,所以

instance变量的值在类的生命周期中永远是唯一的,调用getInstance()方法所得到的返回值永远是同一个。

public class Singleton {
 
    private static Singleton instance = new Singleton();
    
    private Singleton() {
        
    }
    
    public static Singleton getInstance() {
        return instance;
    }
}

懒汉式(线程不安全)
在需要的时候才会创建实例对象,如果已经有实例对象,则会直接返回该对象。

由于该方式只有在需要该对象的时候才会创建实例,所以该方式不会浪费内存,但是这种方式没有考虑到线程安全问题,多个线程同时访问getInstance方法时,可能会导致创建多次实例,因此该方法并不适合在多线程情况下运用。

public class Singleton {
 
    private static Singleton instance = null;
    
    private Singleton() {
        
    }
    
    public static Singleton getInstance() {
        if (instance == null) {

//当多个线程同时调用这个方法的时候,当线程A进入到if判断之后cpu立即切换到B线程,B线程判断到的instance对象也为null,所以B线程也能进入到if里面,然后创建一个对象返回,接下来A线程获得cpu资源时又要创建一个对象,将刚开始B线程创建的对象覆盖掉,所以A线程返回的是另一个对象,这样就出现了线程安全问题,在多线程情况下回返回多个不同的对象。
            instance = new Singleton();
        }
        return instance;
    }
    
}

懒汉式(线程安全)
上述懒汉式单例没有考虑线程安全问题,它是线程不安全的,所以我们在它的基础上加以改进,在getInstance方法上加同步synchronize,保证了懒汉式单例模式的线程安全问题。

public class Singleton {
 
    private static Singleton instance = null;
    
    private Singleton() {
        
    }
    

//在方法上加锁,虽然可以达到线程安全,但是每个线程到了这个位置都要被阻塞,等待前一个线程执行完毕才能继续执行,因为如果当一个线程调用该方法可以直接判断instance对象是否为null,如果不为null,就可以直接返回已有的值,如果在方法上加了这个锁,每个线程只有等到前一个线程执行完方法中的代码后才能够进行判断,就很消耗性能
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

双重校验(Double-Check)
在上一个懒汉式单例模式中,我们为了实现线程安全问题,在方法中加了synchronize关键字,这样虽然保证了线程安全,但是由于每次获取对象都需要同步,会极大的影响性能,因此我们在此基础上进行改进,得到了如下的单例模式。它执行了两次判断null操作,当判断当前对象为空时,对当前操作上锁,然后再判断当前对象是否为空,如果为空,就创建一个新对象,如果不为空,则返回当前对象。

public class Singleton {
 
    private volatile static Singleton instance = null;
    
    private Singleton() {
        
    }
    
    public static Singleton getInstance() {

//第一个if限制的是当判断instance为null时才让它进入if,如果不为null,就直接返回已有的对象
        if (instance == null) {

//在这里就加锁,可以使得所有判断到instance对象为null的线程在这里排队,只有当前一个线程执行完毕之后,下一个线程才接着执行
            synchronized (Singleton.class) {

//这里就现在如果判断拿到instance为null的时候,就进入if判断创建一个对象,并赋值给成员变量,如果前一个线程已经创了一个对象并赋值给成员变量后,后一个线程在判断instance对象时是有值的,所以就不会进入if创建对象,直接返回成员变量就可以了
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

枚举(线程安全)
上述方式都有着共同的缺点:

1.需要额外实现序列化,否则每次反序列化一个对象都会创建一个新的实例。

2.可以使用反射来调用私有构造器。

使用枚举可以完全避免上述行为,它自动支持序列化机制,绝对防止多次实例化,而且更简洁。这种方式是Effective Java作者Josh Bloch提倡的,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。

public enum Singleton {
 
    INSTANCE;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值