手写单例模式
饿汉式加载模式(线程安全)
类一加载就创建对象,这种方式比较常用
优点:线程安全,没有加锁,执行效率高
缺点:不是懒加载,类初始化的时候就加载,浪费内存空间
package com.example.threadpool.Singleton;
public class Singleton1 {
//私有化构造方法
private Singleton1(){}
//定义一个静态变量指向自己类型
private final static Singleton1 instance=new Singleton1();
//对外提供一个公共的方法获取实例
public static Singleton1 getInstance() {
return instance;
}
}
懒汉式加载模式(线程不安全)
线程下使用没有问题,但是多线程无法保证单例
优点:懒加载
缺点:线程不安全
我们的Singleton instance对象是静态变量,我们多线程没有上锁的情况下,可能错误判断instance为null,然后生成多个instance
package com.example.threadpool.Singleton;
/**
* 懒汉式加载模式
*/
public class Singleton2 {
//私有化构造方法
private Singleton2(){}
//定义一个静态变量指向自己的类型
private static Singleton2 instance;
//对外提供一个公共方法来获取实例
public static Singleton2 getInstance(){
if(instance==null){
instance =new Singleton2();
}
return instance;
}
}
懒汉式加载模式(线程安全)
加synchronized关键字在初始化方法上,可以保证线程安全
优点:懒加载,线程安全
缺点:效率低,每一个调用getInstace获取实例的时候我们都需要加锁和释放锁
加锁了,就会保证多线程下创建的仍然是一个对象
package com.example.threadpool.Singleton;
public class Singleton3 {
//私有化构造方法
private Singleton3(){}
//定义一个静态变量指向自己的类型
private static Singleton3 instance;
//对外提供一个公共的方法获取实例
public synchronized static Singleton3 getInstance(){
if(instance==null){
instance=new Singleton3();
}
return instance;
}
}
双重判定锁(线程安全)
我们多线程下会有个二次判断,这样子我们多线程下同时判断为null,但是我们锁住一个线程后有了这个初始化实例,我们第一个判断为null的线程再做一次判断,就可以拿到正确的初始化对象
双重判定锁的优先:
避免不必要的同步开销
在单例模式中,传统实现会将整个 getInstance()
方法同步(即 synchronized
),这样即使单例对象已经创建,仍然会有大量线程竞争锁,导致性能下降。
双重判定锁通过在进入同步块之前添加一次非同步的检查,可以避免每次调用都进行同步(也就是用了一次是否为null的初步判断,减少了我们调用同步方法的线程),只有在实例为 null
的情况下才进入同步
package com.example.threadpool.Singleton;
/**
* 双重判定锁
*/
public class Singleton4 {
//私有化一个构造方法
private Singleton4(){}
//定义一个静态变量指向自己
private static Singleton4 instance;
//对外提供一个公共方法来获取实例
public static Singleton4 getInstance(){
if(instance==null){
synchronized (Singleton4.class){
if(instance==null) {
instance = new Singleton4();
}
}
}
return instance;
}
}