java单例模式

JAVA单例模式

单例模式,所有的操作都只由一个对象来处理,确保一个类只有一个实例,并且易于外部访问,节省资源。

在java类实例化的时候,会使用init方法对实例成分(实例变量、实例代码块)初始化,而后是构造函数的调用,这样就创建了一个对象。

如果想要一个对象只有一个实例,那么就只能调用一次init方法,也就是只能使用一次new方法。

饿汉式

在加载类的时候就创建了单例对象

public class Singletonl {
    private static Singletonl instance = new Singletonl();
    private Singletonl() {}                    //私有构造方法
    public static Singletonl getInstance(){ //不是static方法,无法通过类名.getInstance调用单例
        return instance;
    }
    /*
    性能:加载类的时候就创建了实例,节省时间,占据内存(如果该单例占据空间较大且长时间不用,就不是很好)
    线程:安全
    非延迟加载
     */
}

public class SingleTest {
    public static void main(String[] args) {
        Singletonl a = Singletonl.getInstance();
        System.out.println(a);    // Singletonl@14ae5a5
        Singletonl aa = new Singletonl();   //报错'Singletonl()' has private access in 'Singletonl'
        Singletonl b = Singletonl.getInstance();
        System.out.println(a);    // Singletonl@14ae5a5
    }
}

懒汉式

未加锁

在需要对象的时候才创建,但是线程不安全,当多个线程同时执行到instance == null的判断语句时,会生成多个Singletonl对象,不符合单例的规定。

public class Singletonl {
    private static Singletonl instance = null;
    private Singletonl() {   //私有构造方法

    }
    public static Singletonl getInstance(){
        if (instance == null) {instance = new Singletonl();}
        return instance;
    }
    /*
    懒汉式
    性能:用的时候加载,节省内存
    线程:线程不安全
    延迟加载
               线程1       线程2
    同时判断instance == null   
    同时执行instance = new Singletonl()  生成两个对象
     */
}

加锁(synchronized)

在需要对象的时候才创建,对getInstance()加锁,可以保证每次只有一个线程去实例化对象,但是当对象存在之后,多线程只能串行去获取对象,效率不高。对于已经存在的单例对象,此时所有线程应该都可以获取到对象,但是锁将多线程变为了串行。

public class Singletonl {
    private static Singletonl instance = null;
    private Singletonl() {   //私有构造方法

    }
    public synchronized static Singletonl getInstance(){
        if (instance == null) {instance = new Singletonl();}
        return instance;
    }
    /*
    懒汉式
    性能:在多线程的情况下就变成了串行
    线程:线程不安全
    延迟加载
     */
}

懒汉式+内部加锁(非线程安全)

这种方法也会产生线程不安全的问题,当多个线程执行到判断语句if (instance == null)后,若instance为空,线程a拿到锁,其他线程在等待锁,a初始化对象instance后释放锁,对于其他线程来说,他们由于阻塞到判断语句之后,会认为instance为空,继续初始化,使得单例模式被破坏。

public calss Singletonl{ 
   private static Singletonl instance = null;
    private Singletonl() {   //私有构造方法

    }
    public static Singletonl getInstance(){
        if (instance == null) {
            synchronized (Singletonl.class) {
                instance = new Singletonl();
            }
        }
        return instance;
    }
    /*
    懒汉式 + 同步
    性能:串行
    线程:线程不安全
    延迟加载
               线程1       线程2
    同时判断instance == null
               拿到锁       阻塞
            new instance   阻塞
               释放锁      拿到锁
                         new instance
     */ 
}

懒汉式+两次检测(DCL Double-Check-Lock)----不加volatile

DCL会对instance进行两次检测,避免了创建多个instance的问题,但该方法会有空指针的异常,这是由于java的“智能化”自作主张乱序重拍前后不相关的指令,使得instance还没初始化完就赋值给分配的空间,致使其他线程在使用instance对象的时候报空指针异常。

public class Singletonl {
    private static Singletonl instance = null;
    private Singletonl() {   //私有构造方法

    }
    public static Singletonl getInstance(){
        if (instance == null) {
            synchronized (Singletonl.class) {
                if (instance == null) {
                    instance = new Singletonl();
                }
            }
        }
        return instance;
    }
    /*
    两次检查instance
    性能:串行  速度快一些
    线程:线程安全   空指针异常
    延迟加载
     */
    /*
    这是由于为了加快程序的运行速度, java会对前后指令不互相依赖的指令乱序执行
    java 在new对象的时候,指令顺序为1.分配空间,2.初始化,3.将对象指向空间
    乱序有可能执行顺序是312, 因此instance初始化完成后, 为空指针
    */
}

懒汉式+两次检测(DCL Double-Check-Lock)----加volatile

volatile避免了乱序重排指令,因此可以避免空指针异常

public class Singletonl { 
   private volatile static Singletonl instance = null;   
    private Singletonl() {   //私有构造方法

    }
    public static Singletonl getInstance(){
        if (instance == null) {
            synchronized (Singletonl.class) {
                if (instance == null) {
                    instance = new Singletonl();     // 调用初始化方法
                }
            }
        }
        return instance;
    }
    /*
    两次检查instance  double-check-lock dcl
    性能:串行
    线程:线程安全  无空指针异常风险
    延迟加载
     */
}

静态内部类

jvm隐含的添加了同步机制,在这种情况下,我们就不需要人为的进行同步控制。这些情况为:

  1. 由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时;
  2. 访问final字段时; 
  3. 在创建线程之前创建对象时; 
  4. 线程可以看见它将要处理的对象时。

因此我们可以通过静态初始化器的方法实现单例模式。

public class Singletonl{  
    private Singletonl() {}   // 构造函数

    private static class Holder {
        private static Singletonl instance = new Singletonl();
    }

    public static Singletonl getInstance() {
        return Holder.instance;
    }
    /*
    静态内部类, 静态内部类会延迟加载, 在类装载的时候不会立即执行,
    其他静态方法或成员会在类装载的时候初始化, 而静态内部类是在调用的时候才初始化
    两次检查instance  double-check-lock dcl
    性能:串行
    线程:线程安全  无空指针异常风险
    延迟加载
    */
}

枚举单例

非延迟加载单例模式

public enum Singleton {
    INSTANCE;

    private Singleton() {

    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
    /*
    枚举
    线程安全,非延迟加载
     */
}

延迟加载单例模式

public class Singleton {
    private enum Holder {
        INSTANCE;
        private Singleton instance;
        private Holder (){
            this.instance = new Singleton();
        }
    }

    public static Singleton getInstance() {
        return Holder.INSTANCE.instance;
    }
    /*
    饿汉式静态内部类枚举
    线程安全,非延迟加载
     */
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值