Java单例模式浅谈


核心:保证系统中的一个类只有唯一的一个实例,能为其他对象提供这一实例

Java中的单利主要是保证三点:

1.线程安全 2.不会因为序列化而产生新实例 3.防止反射攻击

常用的两种方式:懒汉式,饿汉式

先说一下懒汉式:

顾名思义,懒汉式是指在方法调用获取实例时才创建实例。

public class Peasant {
    //私有无参构造
    private Peasant() {}
    //声明静态变量节省内存空间,始终用这一个变量
    private static volatile Peasant singleton = null;
    //静态工厂方法 
    public static Peasant getInstance() {
        if (singleton == null) {
            synchronized (Peasant.class) { //同步锁
                if (singleton == null) {//二次校验
                    singleton = new Peasant();

                }
            }
        }

        return singleton;
    }
}

为什么一定要私有无参构造呢?

因为避免类在外部被实例化,私有无参构造后Peasant的唯一实例只能getInstance()访问。自己去创建自己的实例。

为什么变量singleton 要加static修饰呢?

原因很简单,因为要保证单例,必须私有构造。私有构造后就不能对外提供方法获取该类的实例了。但又需要自己去创建自己的实例,这个时候只能通过类名去访问。而类名访问只能用static修饰,而static方法只能访问static成员。所以变量singleton 要加static,getInstance()方法同样需要加static。创建实例的时候Peasant.getInstance(),直接调用即可。

懒汉式如何保证线程安全呢?

在上面的代码中已经体现了,采用双检查锁机制来保证线程的安全。使用synchronized同步,并且进行二次校验。这里需要注意一下 我们使用了volatile来进行修饰。因为不使用volatile的双检查锁机制还是不能保证线程的安全。volatile用来保证变量更新的时候可以通知到其他线程,新的值能立即同步到主内存,以及每次使用前立即从主内存刷新. 当把变量声明为volatile后,编译器与运行时都会认为次变量是共享的.简单的来说被volatile修饰的变量,JVM读取的时候直接从内存读取,跳过CPU的缓存。而非volatile修饰的变量,线程在读取的时候首先从内存拷贝的CPU的缓存中,如果有多个CPU,多线程在运行的时候,那么每个线程可能被不同的CPU处理,线程访问这些变量时,其访问的是这些缓存的变量有可能,并不是内存中实际变量。

最后说一下饿汉式:
public class Peasant {
    //私有无参构造
    private Peasant() {}
    //自行实例化 
    private static final Peasant singleton = new Peasant();
    //静态工厂方法 
    public static Peasant getInstance() {
        return singleton;
    }
}

饿汉式在创建类的同时已经创建好一个静态对象,以后就不会改变了,所以天生线程就是安全的。

总结:

饿汉式:在类创建的同时就已经实例化一个静态对象,即便是你不使用这个对象,它也会占据一定的内存。但是其在第一次调用的时候速度也会很快,因为其资源已在初始化的时候就已经加载完成。线程是天生安全的。
懒汉式:相对饿汉式来说不使用的话并不会占据内存,但第一次调用的时候会进行初始化有延迟,初始化后就和饿汉一样了。就线程安全而言以上代码已解决,但同步之后会影响性能。

最后这里推荐一种适中的单利写法

public class Peasant {
   private Peasant (){}
   //静态内部类
   private static class PeasantLode {
    private static final Peasant MyPeasant = new Peasant();
    }
    public static final Peasant getPeasant() {
        return PeasantLode.MyPeasant;
    }
}

懒汉式通过调用静态内部类的方式进行初始化,这样既保证了线程的安全,又保证了内存的开销。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值