Java单例模式的两种写法(保证线程安全)

Java中实现单例模式有很多种写法

主要介绍两种

饿汉模式和懒汉模式

举例

在计算机中的例子

比如打开一个文件,读取文件的内容并显示出来

饿汉: 把文件的所有内容打印出来并显示

懒汉: 只读取文件的一小部分内容,当用户翻页之后,再读取文件的其他内容,如果不翻页就省下了

假如文件特别大,饿汉文件打开可能需要半天,懒汉可以快速打开

代码举例饿汉模式

直接创建一个唯一的实例化对象

package threading;
class Singleton{
    private static Singleton instance = new Singleton();
    public static Singleton getInstance(){
        return instance;
    }
    private Singleton(){

    }
    
}
public class ThreadDemo10 {
    Singleton singleton = new Singleton();
}

通过将构造方法私有化并且再Singleton方法里面创建唯一实例化对象

会发现无法在外部创建对象

'

打印出来发现是true, 1和2是同一个对象

在多线程下是否安全???

是安全的,多线程情况下只是进行读操作,并不会对这个对象进行修改,只是进行读数据,所以饿汉模式是线程安全的

代码举例创建懒汉模式

package threading;
class SingletonLazy{
    private static SingletonLazy instance;
    public static SingletonLazy getInstance(){
        if(instance == null){
            instance = new SingletonLazy();
        }
        return instance;
    }
    private SingletonLazy(){}
}
public class ThreadDemo11 {
}

这个在多线程下是否能保持安全呢???

涉及到了修改问题,所以可能无法保证创建对象的唯一性

假如n个线程同时调用那就会创建出n个对象,这就不满足我们的单例模式所以要进行一些加锁操作

要对整个进行加锁,保证其是原子操作

否则如果只在

进行加锁,还是会创建出很多个对象

优化

可以进行一小部分的优化

由于加锁解锁操作其实也是很需要时间开销,所以我们可以提前判断一下instance是否为null,如果为null就不需要进行加锁解锁操作,因为此时单纯的读取实例化对象没有涉及到修改,此时是处于线程安全的

package threading;
class SingletonLazy{
    private static SingletonLazy instance;
    public static SingletonLazy getInstance(){
        if (instance == null) {//此处是为了判断对象是否为null,如果为null直接返回无需进行加锁操作节省时间
            synchronized (SingletonLazy.class) {
                if(instance == null){
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
    private SingletonLazy(){}
}
public class ThreadDemo11 {
}

极小概率的一个细节

创建实例化对象的过程中有可能会触发指令重排序

在之前的文章已经说过,实例化对象也可以分成三步骤

1) 创建内存

2) 调用构造方法

3) 把内存地址给引用就是instance

如果触发指令重排序,就会先执行3,再执行2,如果系统调度给线程t1,发现条件不成立是非空的,此时其实还并没有调用构造方法,直接返回实例化对象的引用,就有可能出bug

所有可以加上volatile防止指令重排序

package threading;
class SingletonLazy{
    volatile private static SingletonLazy instance;//防止极小概率指令重排序
    public static SingletonLazy getInstance(){
        if (instance == null) {//此处是为了判断对象是否为null,如果为null直接返回无需进行加锁操作节省时间
            synchronized (SingletonLazy.class) {
                if(instance == null){
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
    private SingletonLazy(){}
}
public class ThreadDemo11 {
}

总结

饿汉模式 : 天生就是安全的,只是进行读操作

懒汉模式 : 不安全,有读也有写

1) 加锁,将 new 和 if 变成原子操作

2) 再次判断 if ,避免不必要的加锁节省开销

3) 使用volatile防止指令重排序发生,保证拿到的一定是完整的实例化对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值