设计模式2-单例模式
一、什么是单例模式?
单例模式:只能创建一个实例化对象,属于创建型模式
注:该类私有构造方法;
该类只能自己创建自己的实例;
实现方式有5种,分别是恶汉模式、懒汉模式,双检锁/双重校验锁,匿名内部类和枚举;
二、实现方式
1.恶汉模式
如同恶汉一样,类初始化同时创建类的实例,由于类的加载机制,在这个类的生命周期只被加载一次,所以保证单例。
优点:不存在线程安全问题。
缺点:创建的类如果没被使用,就会造成内存的浪费
public class Singleton {
//单例模式-恶汉
private static Singleton instance = new Singleton();
private Singleton() { //私有构造方法
}
public static Singleton getInstance(){
return instance;
}
}
2.懒汉模式
只有当自己需要的时候才创建对象。很好的解决内存浪费问题,但是会产生线程安全的问题。
public class Singleton {
//单例模式-懒汉
private static Singleton instance =null;
private Singleton() { //私有构造方法
}
public static Singleton getInstance(){
if(instance == null){ //1
instance = new Singeleton(); //2
}
return instance;
}
}
当线程A和线程B同时进来,走到1的位置,由于都没有初始化实例,所以instace == null 结果是true,这时在2的位置都会实例化对象。
3.双检锁/双重校验锁机制
由于懒汉模式出现线程安全问题,所以需要通过锁机制来解决这个问题。即在方法上加上synchronized关键字。
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
但是这样会大大降低性能,每次进来都先进行加锁,线程越多,性能会越来越慢的。所以采用双重锁机制。
public static Singleton getInstance() {
if (instance == null) { //1
synchronized(Singleton.class){ //2
if (instance == null) { //3
instance = new Singleton(); //4
}
}
}
return instance;
}
1处的判断:是当第一次实例化对象之后,则直接使用之前的对象,没必要执行后面的代码,大大提高了效率
2处判断:当线程A和线程B同时经过1,线程A先获得cpu资源,线程B在2处堵塞,当线程A实例化完成后并释放锁资源,线程B获得锁,此时instance不为空,所以又会实例化一次,所以需要在同步锁里进行判断instance是否为空。
但是上面这段代码也不是绝对线程安全的,这是由于jvm指令重排的原因,我们看一下这段代码,jvm实际帮我们做了什么:
instance = new Singleton()
1. 在堆空间开辟内存空间;
2. 初始化对象和里面的各个参数;
3. 将instance指向对空间的内存地址
这里由于jvm指令重排的原因,可能执行顺序变成1->3->2,当执行执行到第三步的时候,instance不为空了,但并未初始化。
当线程A正在执行4处时,先执行的第三步,此时线程B走到1处判断,由于instance已经不为空了,所以返回未初始化的instance。所以为了解决这个问题,jdk1.6以后引用volatitle。volatitle禁止jvm指令重排,保证了顺序,就不会出现上述问题。
private volatile static Singleton instance = null; //单例对象
4.静态内部类
这种方式既可以解决线程安全的问题,又能解决内存浪费的问题,利用了类的加载机制。
静态内部类,在类初始化的时候不会初始化静态内部类,只有当调用的时候才会,所以不占用内存,只有调用getInstance()方法时才会加载LazyHolder 类,而且之后调用不会加载。
jvm会保证一个类的初始化方法会在多线程环境中正确加锁,同步,如果多个线程去初始化同一个类,只有一个线程执行初始化方法,其他线程会阻塞,注意当一个线程执行完毕初始化方法后,其他线程被唤醒也不会执行初始化方法了,一个类只会初始化一次。
这种也不是最完美的,无法完成传参。
public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
5.枚举单例
类名.字段调用,线程安全
public enum Singleton2 {
INSTANCE;
public void method(){
}
public static void main(String[] args) {
Singleton2 instance = Singleton2.INSTANCE;
Singleton2 instance1 = Singleton2.INSTANCE;
}
}