设计模式之单例模式

1.单例模式的定义

    保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象      的方式,可以直接访问,不需要实例化该类的对象。

1.非线程安全懒汉模式

public class SingletonDemo {

    private static SingletonDemo instance;

    private SingletonDemo(){

    }

    public static SingletonDemo getInstance(){

        if(instance==null){

            instance=new SingletonDemo();

        }

        return instance;

    }

}

2.线程安全懒汉模式

public class SingletonDemo {

    private static SingletonDemo instance;

    private SingletonDemo(){

 

    }

    public static synchronized SingletonDemo getInstance(){

        if(instance==null){

            instance=new SingletonDemo();

        }

        return instance;

    }

}

3.饿汉模式

直接在运行这个类的时候进行一次loading,之后直接访问。显然,这种方法没有起到lazy loading的效果,考虑到前面提到的和静态类的对比,这种方法只比静态类多了一个内存常驻而已。

public class SingletonDemo {

    private static SingletonDemo instance=new SingletonDemo();

    private SingletonDemo(){

 

    }

    public static SingletonDemo getInstance(){

        return instance;

    }

}

4. 静态类内部加载

使用内部类的好处是,静态内部类不会在单例加载时就加载,而是在调用getInstance()方法时才进行加载,达到了类似懒汉模式的效果,而这种方法又是线程安全的。

public class SingletonDemo {

    private static class SingletonHolder{

        private static SingletonDemo instance=new SingletonDemo();

    }

    private SingletonDemo(){

        System.out.println("Singleton has loaded");

    }

    public static SingletonDemo getInstance(){

        return SingletonHolder.instance;

    }

}

 

5.双重锁校验模式

public class SingletonDemo {

    private volatile static SingletonDemo instance;

    private SingletonDemo(){

        System.out.println("Singleton has loaded");

    }

    public static SingletonDemo getInstance(){

        if(instance==null){

            synchronized (SingletonDemo.class){

                if(instance==null){

                    instance=new SingletonDemo();

                }

            }

        }

        return instance;

    }

}

接下来我解释一下在并发时,双重校验锁法会有怎样的情景:

STEP 1. 线程A访问getInstance()方法,因为单例还没有实例化,所以进入了锁定块。

STEP 2. 线程B访问getInstance()方法,因为单例还没有实例化,得以访问接下来代码块,而接下来代码块已经被线程1锁定。

STEP 3. 线程A进入下一判断,因为单例还没有实例化,所以进行单例实例化,成功实例化后退出代码块,解除锁定。

STEP 4. 线程B进入接下来代码块,锁定线程,进入下一判断,因为已经实例化,退出代码块,解除锁定。

STEP 5. 线程A初始化并获取到了单例实例并返回,线程B获取了在线程A中初始化的单例。

理论上双重校验锁法是线程安全的,并且,这种方法实现了lazyloading。

 

懒汉模式与饿汉模式区别

饿汉模式是最简单的一种实现方式,饿汉模式在类加载的时候就对实例进行创建,实例在整个程序周期都存在。它的好处是只在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。它的缺点也很明显,即使这个单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。

双重校验锁方法与线程安全的懒汉模式区别

    可以看到上面在同步代码块外多了一层instance为空的判断。由于单例对象只需要创建一次,如果后面再次调用getInstance()只需要直接返回单例对象。因此,大部分情况下,调用getInstance()都不会执行到同步代码块,从而提高了程序性能。不过还需要考虑一种情况,假如两个线程A、B,A执行了if (instance == null)语句,它会认为单例对象没有创建,此时线程切到B也执行了同样的语句,B也认为单例对象没有创建,然后两个线程依次执行同步代码块,并分别创建了一个单例对象。为了解决这个问题,还需要在同步代码块中增加if(instance == null)语句,也就是上面看到的代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值