ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

在多线程环境中的单例模式

2021-09-18 20:35:29  阅读:109  来源: 互联网

标签:Singleton getInstance singleton 模式 实例 static private 单例 多线程


在多线程环境中的单例模式

目前有三种实现单例模式的方式:

  1. 饿汉模式
  2. 懒汉模式
  3. 静态内部类

1、饿汉模式

class Singleton{
    // 第一步 构造器私有化
    private Singleton(){}
    // 第二步 自行对外提供实例
    private static final Singleton singleton = new singleton();
    // 第三步 提供外界可以获取实例的方法
    public static Singleton getInstance(){
        return singleton;
    }
}

在多线程的情况下,饿汉模式的性能是非常好的,因为获取实例的方法只是简单的返回实例,并没有任何的锁操作,因此在并行的程序中会有良好的表现。

但是这种方式有这明显的不足,就是实例在什么时候创建是不受控制的,对于静态成员singleton,它会在类第一次初始化的时候被创建。这个时刻并不一定是getInstance()第一次被调用的时候。

当类初始化的时候,实例就会被创建。如果希望精确控制singleton的创建时间,可以采用支持延迟加载策略的方法。这种方法只会在第一次调用getInstance()方法的时候才会创建对象。

2. 懒汉模式

class Singleton{
    // 第一步 构造器私有化
    private Singleton(){}
    // 第二步 创建实例的引用
    private static final Singleton singleton = null;
    // 第三步 提供外界可以获取实例的方法,此时需要判断一下引用的状态。
    public static Singleton getInstance(){
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

懒汉模式的实现思想如下:最初,我们并不需要实例化singleton,而当getInstance()方法第一次被调用的时候,创建单例对象。

但是这个模式有一个特别明显的问题,就是没有考虑多线程下的安全问题,在多线程并发的情况下,会并发调用获取实例方法,从而导致系统同时创建多个单例类实例,显然不符合要求。可以通过给getInstance()方法加锁才行。

class Singleton{
    // 第一步 构造器私有化
    private Singleton(){}
    // 第二步 创建实例的引用
    private static final Singleton singleton = null;
    // 第三步 提供外界可以获取实例的方法,此时需要判断一下引用的状态。
    public static synchronized Singleton getInstance(){
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

添加synchronized锁虽然可以保证线程安全,但是锁的范围过大,单例类是系统唯一的,在创建出来实例后,后续再加锁就只会造成性能的浪费。需要对代码进行优化,所以又有了双重校验锁的模式。

class Singleton{
    // 第一步 构造器私有化
    private Singleton(){}
    // 第二步 创建实例的引用
    private static final Singleton singleton = null;
    // 第三步 提供外界可以获取实例的方法,此时需要判断一下引用的状态。
    public static synchronized Singleton getInstance(){
        // 第一次校验
        if(singleton==null){
        	synchronized(Singleton.class){
                // 第二次校验
                if(singleton==null){
            		singleton = new Singleton();
                }
        	}
    	}
    	return singleton;
    }
    
}

singleton = new Singleton();并不是原子操作,所以双重校验锁会出现指令重排序问题。可能导致在实例还没又初始化完成之前,就被其他线程使用的问题,可以使用volatile关键字来修饰singleton字段,可以禁止指令的重排序优化。

class Singleton{
    // 第一步 构造器私有化
    private Singleton(){}
    // 第二步 创建实例的引用
    private static volatile Singleton singleton = null;
    // 第三步 提供外界可以获取实例的方法,此时需要判断一下引用的状态。
    public static synchronized Singleton getInstance(){
        // 第一次校验
        if(singleton==null){
        	synchronized(Singleton.class){
                // 第二次校验
                if(singleton==null){
            		singleton = new Singleton();
                }
        	}
    	}
    	return singleton;
    }
    
}

3. 静态内部类

class Singleton{
    // 第一步 构造器私有化
    private Singleton(){}
    //第二步 创建静态内部类
    private static class help{
        private static Singleton singleton = new Singleton();
    }
    // 第三步 提供外界可以获取实例的方法
    public static Singleton getInstance(){
        return help.singleton;
    }
}

可见,双重检查模式是一种丑陋,复杂的方法,而在静态内部类实现方式却可以完美拥有前两种方式的优点,首先getInstance()方法中没有锁,这使得在高并发环境下性能优越。其次,只有在getInstance()才会创建实例,因为这种方法巧妙的使用了内部类和类的初始化方式,私有化内部类,使得我们不可能在外部访问并初始化它。而我们只能在getInstance()内部才能对内部类初始化,利用虚拟机的类初始化机制来创建单例。

标签:Singleton,getInstance,singleton,模式,实例,static,private,单例,多线程
来源: https://www.cnblogs.com/SYXblogs/p/15310084.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有