189 8069 5689

【JavaEE】单例模式(饿汉&懒汉)-创新互联

目录

我们一直强调成都网站建设、网站设计对于企业的重要性,如果您也觉得重要,那么就需要我们慎重对待,选择一个安全靠谱的网站建设公司,企业网站我们建议是要么不做,要么就做好,让网站能真正成为企业发展过程中的有力推手。专业网站制作公司不一定是大公司,创新互联作为专业的网络公司选择我们就是放心。

前言:

单线程下的单例模式

饿汉模式

懒汉模式

多线程下的单例模式

懒汉模式的修改

1.加锁

2.有条件的加锁

3.解决内存可见性和指令重排序


前言:

本片文章介绍设计模式中的一个模式——单例模式。

单例模式就是只允许创建出一个实例的类。比如之前在使用JDBC编程的时候的DataSource这个类,就可以使用单例模式。见这篇文章http://t.csdn.cn/uq1lR。

其中单例模式有很多种实现方法,这里只用懒汉模式和饿汉模式来实现单例模式。


单线程下的单例模式

单线程中,没有线程安全问题,其代码会简单很多。

饿汉模式

饿汉模式体现出一个字——急。因为这个实例直接就是在类加载阶段就被创建出来。

class SingletonHungry{

    // 用static修饰,这样在类加载阶段就有了这个实例
    private static SingletonHungry instance = new SingletonHungry();

    // 用静态公开方法返回实例
    public static SingletonHungry getInstance() {
        return instance;
    }

    // 为了防止实例化多个对象,把构造方法用private修饰
    private SingletonHungry(){};

}

public class SingletonTest1 {

    public static void main(String[] args) {

        SingletonHungry instance1 = SingletonHungry.getInstance();
        SingletonHungry instance2 = SingletonHungry.getInstance();
        System.out.println(instance1 == instance2);

        //SingletonHungry instance3 = new SingletonHungry();

    }

}


懒汉模式

懒汉模式突出一个字——懒。这个实例是只有调用获取实例方法的时候才创建出来。

class SingletonLazy{
    private static SingletonLazy instance = null;

    // 只有在调用了该方法后才创造出了实例
    public static SingletonLazy getInstance() {
        // 如果已经创建了该实例,就直接返回之前的实例
        if (instance == null) {
            instance =  new SingletonLazy();
        }
        return instance;
    }

    // 为了防止实例化多个对象,把构造方法用private修饰
    private SingletonLazy(){}
}

public class SingletonTest2 {

    public static void main(String[] args) {

        SingletonLazy instance1 = SingletonLazy.getInstance();
        SingletonLazy instance2 = SingletonLazy.getInstance();
        System.out.println(instance1 == instance2);

        //SingletonLazy instance3 = new SingletonLazy();
    }

}

代码结果如上。 


多线程下的单例模式

在上述代码中,如果考虑多线程的话

饿汉模式没有线程安全问题,它直接就是在类加载是创建出了一个实例,使用该实例也没有其他修改的操作,直接返回即可。

懒汉模式是有线程安全问题。它又要判断比较实例是否为null,又要创建一个实例,最后才返回实例。其中对于实例是由修改的操作的。只要有修改操作,就可能会有线程安全问题。如下图:

懒汉模式的修改 1.加锁

对于这种又有读,又有写的操作,保持原子性——加锁即可。

public SingletonThread getInstance() {
        // 这里对于load cmp new这几步加锁,保持了其原子性
        synchronized (SingletonThread.class) {
            if (instance == null) {
                instance = new SingletonThread();
            }
        }
        return instance;
    }
2.有条件的加锁

但是加锁是一种开销比较大的操作,上述加锁操作并不是每次都要加锁的。如果已经创建了实例,直接返回实例即可。

public SingletonThread getInstance() {
        // 如果实例未被创建,用加锁的方法创建实例
        // 如果创建,直接返回
        if (instance == null) {
            // 这里对于load cmp new这几步加锁,保持了其原子性
            synchronized (SingletonThread.class) {
                if (instance == null) {
                    instance = new SingletonThread();
                }
            }
        }
        return instance;
    }
3.解决内存可见性和指令重排序

内存可见性:因为instance要读取并修改,所以对于内存可见性的问题也要预防。

指令重排序:

上面的new实例的指令又分为三个顺序步骤:

①申请内存空间

②调用构造方法,实例化一个对象

③把内存空间的地址赋值给这个对象

这几步可能可能会变成①③②。单线程下没有问题,但是多线程就会有问题了。

要想解决这两个问题,使用volatile关键字修饰instance即可。

private volatile static SingletonThread instance = null;

完整的懒汉模式的线程安全代码如下:

// 修改懒汉模式,使其线程安全
class SingletonThread {
    // 使用volatile解决内存可见性和指令重排序问题
    private volatile static SingletonThread instance = null;

    public SingletonThread getInstance() {
        // 如果实例未被创建,用加锁的方法创建实例
        // 如果创建,直接返回
        if (instance == null) {
            // 这里对于load cmp new这几步加锁,保持了其原子性
            synchronized (SingletonThread.class) {
                if (instance == null) {
                    instance = new SingletonThread();
                }
            }
        }
        return instance;
    }

}

有什么问题评论区指出。希望可以帮到你。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


网站栏目:【JavaEE】单例模式(饿汉&懒汉)-创新互联
分享地址:http://cdxtjz.cn/article/djicgd.html

其他资讯