Java单例模式


前言

Java其实有23种设计模式,这里简单写一下单例模式,单例模式主要是保证该类在实例化的时候,最多只生成一个实例,按照类型,可以分为懒汉式和饿汉式。


一、饿汉式

顾名思义,饿汉式是指的该类还没有被调用的时候就初始化实例,调用的时候直接拿出来,下面我们先简单写一个例子:

//饿汉式
public class Singleton {
    private static final Singleton singleton = new Singleton();
    private Singleton(){
    }
	public static Singleton getSingleton(){
		return singleton;
	}
}

饿汉式由于直接实例化,所以在多线程的场景中并不存在线程安全的问题,缺点就是类未被调用的时候就初始化实例,会造成内存资源的浪费。

二、懒汉式

顾名思义,懒汉式就是突出一个懒字,他是在被调用的时候才不情不愿的初始化一个实例出来,下面我们先简单写一个例子:

代码如下:

public class Singleton {
    private static Singleton singleton;

    private Singleton(){};

    public Singleton getSingleton(){
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

这样的方式在单线程的场景下是没有问题的,只会生成一个实例,但是如果是在多线程的场景下,就会存在问题,比如:
A线程判断singleton==null为true,这个时候他会进入判断里面执行new Singleton();而就在执行这个语句前,好巧不巧,刚好B线程也来凑热闹,首先也是判断 singleton是否是null,由于这个时候A线程的初始化实例还没开始,所以B线程也会进来实例化一个对象,这样就违背了我们该类只能初始化一个实例的初衷,这个时候我们改进一下代码:

public class Singleton {
    private static Singleton singleton;

    private Singleton(){};

    public Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}

在这里示例中,我们加入了synchronized 锁,这样就能保证B线程一定是在A线程实例化对象后再执行,但是我们也能意识到这样的代码和之前的会遇到相同的问题,虽然加了锁,但是B线程等待锁执行完成后,还是会再初始化一个实例出来,所以我们这里还得再加一个二次校验。

public class Singleton {
    private static Singleton singleton;

    private Singleton(){};

    public Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

这样看起来,我们的程序写的好像没什么问题了,但是在多线程的场景下,还会涉及到一个问题,那就是共享变量对不同的线程是不可见的,只有等修改后的共享变量刷新到主内存,该变量才会对其他线程可见。所以我们这里A线程执行了new Singleton()实例化一个对象,但此时,针对B线程来说,他不会立马知道已经初始化成功了一个对象,此时他还是会去实例化一个新的对象,所以针对这个问题,Java为我们提供了一个关键字volatile ,我们来写一下最终版示例:

public class Singleton {
    private static  volatile Singleton singleton;

    private Singleton(){};

    public Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

Singleton 被volatile修饰后,该变量只要被修改,会里面写入主内存,对其他线程可见,所以当A线程执行了new Singleton()操作,B线程这个时候就知道该类已经实例化了,就不会再new新的实例了。


总结

饿汉式:在多线程场景下不会存在线程安全问题,但是由于一开始就初始化实例,会造成内存资源的 浪费。
懒汉式:在多线程场景下会存在线程安全问题,但是我们通过引入synchronized锁和volatile关键字来解决该方式下的线程安全问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值