如何实现单例模式

什么是单例模式

单例模式就是这个类的对象只会被创建一次也就是只会被实例一次。就类似与你只能娶一个老婆,但是可以生很多孩子,毕竟政策放开了。那有的人会说为什么会用模式来形容,我写代码的时候把这个类对象只new一次不就行了?注意这需要看程序员本身的技术,是需要人为来控制的,众所周知人是不靠谱的。那单例模式之中又分为饿汉模式和懒汉模式。具体的实现如下面代码所示。

饿汉模式(我直接就创建这个类的实例)


 class Singleton {
    private static Singleton instance = new Singleton();

    //外部获取到这个实例的方法
     public static Singleton getSingleton() {
         return instance;
     }
     //不允许外部再创建这个实力,所以将该类的构造方法修饰符改为private
     private Singleton() {

    }
 }
public class Test4 {

    public static void main(String[] args) {
        Singleton singleton = Singleton.getSingleton();
    }

}

懒汉模式(等我调用的时候才会创建这个类的实例)


class SingleLazy {
    private static SingleLazy instance = null;

    public static SingleLazy getInstance() {
        if (instance == null) {
            instance = new SingleLazy();
        }
        return instance;
    }

    private SingleLazy() {

    }
}
public class Test5 {


    public static void main(String[] args) {
        SingleLazy singleLazy = SingleLazy.getInstance();
    }

}

但是这里引出了一个问题,多线程中,懒汉模式是线程不安全的,因为这里涉及到多个线程同时修改一个变量。因为这里的if判断操作和new对象不是原子性的,所以这里需要加锁。下面是优化的代码。


class SingleLazy {
    private static SingleLazy instance = null;

    public static SingleLazy getInstance() {
        synchronized (SingleLazy.class) {
            if (instance == null) {
                instance = new SingleLazy();
            }
        }
        return instance;
    }

    private SingleLazy() {

    }
}
public class Test5 {
    public static void main(String[] args) {
        SingleLazy singleLazy = SingleLazy.getInstance();
    }

}

到这里线程是安全了,同时又引出了一个新的问题。就是如果只调用一次getInstance没问题,但是多次调用可能会影响性能。因为我们其实在第一次调用getInstance这个方法时加锁就可以了,像我们上述代码一样写的话,每次调用该方法都会加锁。那有没有让懒汉模式既线程安全,同时又不需要每次都加锁,因为加锁很影响性能。兄弟,有的,有的。


class SingleLazy {
    private static SingleLazy instance = null;

    public static SingleLazy getInstance() {
        if (instance == null) {
            synchronized (SingleLazy.class) {
                if (instance == null) {
                    instance = new SingleLazy();
                }
            }
        }
        return instance;
    }

    private SingleLazy() {

    }
}
public class Test5 {
    public static void main(String[] args) {
        SingleLazy singleLazy = SingleLazy.getInstance();
    }

}

我们这里的思路是我第一次创建对象的时候需要加锁,那我们第二次调用的时候如果对象已经被创建出来了,那我们就不需要进行加锁了,已经有了对象,直接返回就可以了。所以这里可以加上一个判断,判断对象是否为空。那写到这里就没问题了吗?兄弟有的,有的。这里会涉及到指令重排序的情况,那什么是指令重排序呢?

指令重排序是编译器在不影响整体逻辑的情况,对于代码的执行顺序进行调整,从而优化性能的操作。那我们上述代码中在创建对象的时候就可能出现指令重排序。创建对象的时候粗略可以分为三步,1.在内存中申请空间2.在内存中构造对象3.将该对象的地址返回给instance引用对象.所以就会出现当两个线程同事访问的时候,其中一个线程正在创建对象,但是只执行了1和3,2还没执行到呢,但是这个时候另一个线程执行了第一个if判断instance不为空,但是不合法,直接return了,这个时候就会出现问题。要想解决这个问题也很容易,利用volatile修饰instance就可以了。


class SingleLazy {
    private static volatile SingleLazy instance = null;

    public static SingleLazy getInstance() {
        if (instance == null) {
            synchronized (SingleLazy.class) {
                if (instance == null) {
                    instance = new SingleLazy();
                }
            }
        }
        return instance;
    }

    private SingleLazy() {

    }
}
public class Test5 {
    public static void main(String[] args) {
        SingleLazy singleLazy = SingleLazy.getInstance();
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值