前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式-单例模式

设计模式-单例模式

原创
作者头像
用户9322266
发布2022-10-09 18:28:12
2000
发布2022-10-09 18:28:12
举报
文章被收录于专栏:MY

概念

单例模式确保每个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

特点

  • 单例类只有一个实例
  • 单例类必须自己创建自己的一个唯一实例
  • 必须给其他对象提供这个实例好处
  • 确保全局对象的唯一性。比如系统启动读取配置文件就需要单例保证配置一致性。
  • 节省资源

线程安全问题

获取单例时,不能产生多个实例对象;

使用单例时,注意单例对象内的实例变量会被多线程共享,会有线程安全问题。

线程安全问题解决

无状态对象,阻塞式创建

实现单例模式

1. 饥汉式(静态变量)

' // 单例类

代码语言:txt
复制
public class Singleton{
代码语言:txt
复制
    // 类的唯一实例,静态变量, final,没有线程安全问题
代码语言:txt
复制
    private final static Singleton instance = new Singleton();
代码语言:txt
复制
    private Singleton(){}
代码语言:txt
复制
    public static Singleton getInstance(){
代码语言:txt
复制
        return instance;
代码语言:txt
复制
    }
代码语言:txt
复制
}

'

优点

写法简单;类装载时完成实例化;避免线程同步问题。

缺点

  • 因为类装载时实例化,没有达到懒装载(延时加载)效果;
  • 若一直不用,内存浪费。饥汉式(静态代码块)

'// 单例类

代码语言:txt
复制
public class Singleton{
代码语言:txt
复制
    // 类的唯一实例,静态代码块处实例化,类装载时执行静态块初始实例 
代码语言:txt
复制
    final,没有线程安全问题
代码语言:txt
复制
    private  static Singleton instance;
代码语言:txt
复制
    static{
代码语言:txt
复制
        instance = new Singleton();
代码语言:txt
复制
    }
代码语言:txt
复制
    private Singleton(){}
代码语言:txt
复制
    public static Singleton getInstance(){
代码语言:txt
复制
        return INSTANCE;
代码语言:txt
复制
    }
代码语言:txt
复制
}

'

优缺点同上

2. 懒汉式(线程不安全,不可用)

' // 单例类

代码语言:txt
复制
public class Singleton{
代码语言:txt
复制
    // 代码执行判断没有创建实例时创建唯一实例
代码语言:txt
复制
    final,没有线程安全问题
代码语言:txt
复制
    private  static Singleton instance;
代码语言:txt
复制
    private Singleton(){}
代码语言:txt
复制
    public static Singleton getInstance(){
代码语言:txt
复制
        if(instance == null){
代码语言:txt
复制
            instance = new Singleton();
代码语言:txt
复制
        }
代码语言:txt
复制
        return instance;
代码语言:txt
复制
    }
代码语言:txt
复制
}

'

优点

懒加载,使用时,创建;

缺点

多线程下不能使用,多线程会创建多个实例,A进入if判断,未往下执行,B也判断。产生多个实例。

3. 懒汉式(线程安全,同步方法,不推荐)

' // 单例类

代码语言:txt
复制
public class Singleton{
代码语言:txt
复制
    // 代码执行判断没有创建实例时创建唯一实例
代码语言:txt
复制
    private  static Singleton instance;
代码语言:txt
复制
    private Singleton(){}
代码语言:txt
复制
    public static synchronized Singleton getInstance(){
代码语言:txt
复制
        if(instance == null){
代码语言:txt
复制
            instance = new Singleton();
代码语言:txt
复制
        }
代码语言:txt
复制
        return instance;
代码语言:txt
复制
    }
代码语言:txt
复制
}

'

特点

对上一种的可能引发同步问题的方法加了sychronized上锁。

阻塞式同步。

效率低,每个线程想获得实例时,执行get都要同步,实际上实例化只要一次就行,获取直接return就好。

4. 懒汉式(线程安全,同步代码块,不可用)

' // 单例类

代码语言:txt
复制
public class Singleton{
代码语言:txt
复制
    // 代码执行判断没有创建实例时创建唯一实例
代码语言:txt
复制
    private  static Singleton instance;
代码语言:txt
复制
    private Singleton(){}
代码语言:txt
复制
    public static  Singleton getInstance(){
代码语言:txt
复制
        if(instance == null){
代码语言:txt
复制
            sychronized(Singleton.class){
代码语言:txt
复制
                instance = new Singleton();
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
        return instance;
代码语言:txt
复制
    }
代码语言:txt
复制
}

'

特点

无法同步,可能产生多个实例

5. 双重检查(推荐用)

有些人认为使用 volatile 的原因是可见性,也就是可以保证线程在本地不会存有 uniqueInstance 的副本,每次都是去主内存中读取。但其实是不对的。使用 volatile 的主要原因是其另一个特性:禁止指令重排序优化。

也就是说,在 volatile 变量的赋值操作后面会有一个内存屏障(生成的汇编代码上),读操作不会被重排序到内存屏障之前。比如上面的例子,取操作必须在执行完 1-2-3 之后或者 1-3-2 之后,不存在执行到 1-3 然后取到值的情况。从「先行发生原则」的角度理解的话,就是对于一个 volatile 变量的写操作都先行发生于后面对这个变量的读操作(这里的“后面”是时间上的先后顺序)。

' // 单例类

代码语言:txt
复制
public class Singleton{
代码语言:txt
复制
    // 两次if,这样实例化只做一次,后面get时只return实例化对象。用volatile主要是因为new在JVM中做了3件事:1分配内存,2调用构造初始化成员变量形成实例(非null),3非将对象指向分配的内存空间(初始化完成)。JVM中有指令重排,顺序1-2-3或1-3-2.如果是后者,在3-2之间被另一线程抢占了,instance已经是非null,但是并没有初始化,另一线程会直接返回instance然后使用,这是不安全的。
代码语言:txt
复制
    private  static volatile Singleton instance;
代码语言:txt
复制
    private Singleton(){}
代码语言:txt
复制
    public static  Singleton getInstance(){
代码语言:txt
复制
        if(instance == null){
代码语言:txt
复制
            sychronized(Singleton.class){
代码语言:txt
复制
                if(instance == null){
代码语言:txt
复制
                    instance = new Singleton();
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
        return instance;
代码语言:txt
复制
    }
代码语言:txt
复制
}

'

优点

懒加载

线程安全

效率较高

6. 静态内部类(推荐用)

' // 单例类

代码语言:txt
复制
public class Singleton{
代码语言:txt
复制
    private Singleton(){} //私有,只有get方法能访问,读的时候不会同步
代码语言:txt
复制
    private static  class SingletonIinstance{
代码语言:txt
复制
        private static final Singleton instance = new Singleton();
代码语言:txt
复制
    }
代码语言:txt
复制
    public static Singleton getInstance() {
代码语言:txt
复制
        return SingletonIinstance.instance;
代码语言:txt
复制
    }     
代码语言:txt
复制
}

'

优点

延时加载

效率高

线程安全

7. 枚举(推荐)

JDK1.5的枚举实现大力,避免同步问题,避免反序列化重现创建新的对象

' //枚举

代码语言:txt
复制
public enumerate Singleton{
代码语言:txt
复制
    INSTANCE;
代码语言:txt
复制
}   

'

单例注意

使用相应的获取对象的方法,不要使用new

使用场景

频繁创建和销毁额对象

工具类对象

频繁访问数据库或文件的对象

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概念
  • 特点
  • 线程安全问题
  • 线程安全问题解决
  • 实现单例模式
    • 1. 饥汉式(静态变量)
      • 优点
      • 缺点
      • 优缺点同上
    • 2. 懒汉式(线程不安全,不可用)
      • 优点
      • 缺点
    • 3. 懒汉式(线程安全,同步方法,不推荐)
      • 特点
    • 4. 懒汉式(线程安全,同步代码块,不可用)
      • 特点
    • 5. 双重检查(推荐用)
      • 优点
    • 6. 静态内部类(推荐用)
      • 优点
    • 7. 枚举(推荐)
    • 单例注意
    • 使用场景
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档