Exploring
首页
  • Java

    • 面向对象的思想OOP
    • 浅谈Java反射原理
    • endorsed覆盖JDK中的类
  • 认证与授权

    • LDAP概念和原理介绍
    • OAuth2介绍
  • Impala

    • Impala 介绍
  • MySQL

    • 关于MySQL的一些面试题
    • 解决MySQL不到中文数据
    • 数据库之事务与实现原理
  • Oracle

    • oracle的表空间,用户管理,表操作,函数
    • oracle的查询、视图、索引
    • plsql简单入门
  • Redis

    • 数据类型详解
    • 跳越表
    • 数据持久化的两种方式
  • 共识算法

    • gossip
  • RPC

    • GRPC初识与快速入门
    • ProtocolBuffer基本语法
  • RabbitMQ

    • RabbitMQ入门程序之HelloWorld
    • RabbitMQ之工作模式
  • Zookeeper

    • Zookeeper一文入门
  • Docker

    • Docker入门初体验
  • Maven

    • 把自己的包到Maven中央仓库
    • Maven之自定义插件
  • Nginx

    • nginx的安装
    • nginx的配置文件
    • nignx 的变量
  • Tomcat

    • Servlet3通过SPI进行注册组件
  • Vagrant

    • vagrant 初始化
    • vagrant 常用配置
    • vagrant 自己制作 box
  • Linux

    • 启动方式 Systemd
    • 后台服务
    • 防火墙与 Iptables
  • 设计模式

    • 设计模式-代理
    • 设计模式-单例模式
    • 设计模式-迭代器
  • 分布式

    • CAP 理论
  • 数据结构

    • 数据结构之堆Heap
    • 数据结构之哈希表
    • 数据结构之队列
  • 计算机网络

    • HTTP与HTTPS详解
    • 浅谈DNS协议
    • ISP中的网络层
  • 算法

    • 常用查找算法及Java实现
    • 常用排序算法及Java实现
    • 迪杰斯特拉算法
  • 操作系统

    • 操作系统之进程调度算法
    • 操作系统之进程通讯IPC
    • 操作系统之内存管理
  • 抓包

    • 生成安卓系统证书
  • 加解密

    • 常见加密算法
    • 公开秘钥基础知识
    • RSA 解析
  • Windows

    • scoop 包管理
    • windows-terminal 配置
    • 增强 PowerShell
归档
Github (opens new window)
首页
  • Java

    • 面向对象的思想OOP
    • 浅谈Java反射原理
    • endorsed覆盖JDK中的类
  • 认证与授权

    • LDAP概念和原理介绍
    • OAuth2介绍
  • Impala

    • Impala 介绍
  • MySQL

    • 关于MySQL的一些面试题
    • 解决MySQL不到中文数据
    • 数据库之事务与实现原理
  • Oracle

    • oracle的表空间,用户管理,表操作,函数
    • oracle的查询、视图、索引
    • plsql简单入门
  • Redis

    • 数据类型详解
    • 跳越表
    • 数据持久化的两种方式
  • 共识算法

    • gossip
  • RPC

    • GRPC初识与快速入门
    • ProtocolBuffer基本语法
  • RabbitMQ

    • RabbitMQ入门程序之HelloWorld
    • RabbitMQ之工作模式
  • Zookeeper

    • Zookeeper一文入门
  • Docker

    • Docker入门初体验
  • Maven

    • 把自己的包到Maven中央仓库
    • Maven之自定义插件
  • Nginx

    • nginx的安装
    • nginx的配置文件
    • nignx 的变量
  • Tomcat

    • Servlet3通过SPI进行注册组件
  • Vagrant

    • vagrant 初始化
    • vagrant 常用配置
    • vagrant 自己制作 box
  • Linux

    • 启动方式 Systemd
    • 后台服务
    • 防火墙与 Iptables
  • 设计模式

    • 设计模式-代理
    • 设计模式-单例模式
    • 设计模式-迭代器
  • 分布式

    • CAP 理论
  • 数据结构

    • 数据结构之堆Heap
    • 数据结构之哈希表
    • 数据结构之队列
  • 计算机网络

    • HTTP与HTTPS详解
    • 浅谈DNS协议
    • ISP中的网络层
  • 算法

    • 常用查找算法及Java实现
    • 常用排序算法及Java实现
    • 迪杰斯特拉算法
  • 操作系统

    • 操作系统之进程调度算法
    • 操作系统之进程通讯IPC
    • 操作系统之内存管理
  • 抓包

    • 生成安卓系统证书
  • 加解密

    • 常见加密算法
    • 公开秘钥基础知识
    • RSA 解析
  • Windows

    • scoop 包管理
    • windows-terminal 配置
    • 增强 PowerShell
归档
Github (opens new window)
  • 设计模式

    • 设计模式-代理
    • 设计模式-单例模式
      • 单例模式概念
      • 单例的实现方式
        • 饿汉式
        • 懒汉式(线程不安全)
        • 饿汉式 方法锁(线程安全、效率低)
        • 饿汉式 Double CheckLock 双重校验锁(线程安全、效率高)
        • 饿汉式 静态内部类
        • 枚举方式
      • 如何选用
    • 设计模式-迭代器
    • 设计模式-工厂
    • 设计模式-观察者
    • 设计模式-模板方法
    • 设计模式-生成器
    • 设计模式-适配器
    • 设计模式-外观模式
    • 设计模式-原型
    • 设计模式-责任链
    • 设计模式-装饰器
  • 分布式

  • 方法论
  • 设计模式
unclezs
2020-07-23
0
目录

设计模式-单例模式

# 单例模式概念

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。 Java中的Runtime即是单例。

# 单例的实现方式

# 饿汉式

Runtime类也是使用的饿汉式。

//饿汉式
public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
         return instance;  
    }  
}
1
2
3
4
5
6
7
8

# 懒汉式(线程不安全)

//懒汉模式
public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}
1
2
3
4
5
6
7
8
9
10
11
12

# 证明不安全

这种方法会存在一个问题就是,在并发情况下无法保证单例。比如两个线程同时运行到 if (Instance == null) 的时候,这个时候因为对象并未实例化,所以都是得到true.这个时候就会创建两个对象了,不能保证单例。

public class Test {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(SingleTon.getInstance());
            }
        }, "线程1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(SingleTon.getInstance());
            }
        }, "线程2").start();

    }
}
//多运行几次得到
SingleTon@6d0ca4c7
SingleTon@455d5a7e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 饿汉式 方法锁(线程安全、效率低)

因为每次执行都是同步的,所以效率比较低

public class SingleTon {
    private SingleTon(){};
    private static SingleTon Instance;

    public synchronized static SingleTon getInstance() {
        if (Instance == null) {
            Instance = new SingleTon();
        }
        return Instance;
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 饿汉式 Double CheckLock 双重校验锁(线程安全、效率高)

public class SingleTon {
    private SingleTon(){};
    private volatile static SingleTon Instance;

    public  static SingleTon getInstance() {
        if (Instance == null) {
            synchronized (SingleTon.class) {
                if (Instance == null) {
                    Instance = new SingleTon();
                }
            }
        }
        return Instance;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

需要注意 Instance 采⽤ volatile 关键字修饰也是很有必要。 Instance 采⽤ volatile 关键字修饰也是很有必要的, Instance = new Singleton(); 这段代码其实是分为三步执⾏: 1. 分配内存空间 2. 初始化 3. 将Instance指向分配的内存地址(这个时候已经不为null了)

但是由于 JVM 具有指令重排的特性,执⾏顺序有可能变成 1→3→2。 指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致⼀个线程获得还没有初始化的实例。 例如,线程 T1 执⾏了 1 和 3,此时 T2 调⽤ getInstance() 后发现 Instance 不为空,因此返回Instance, 但此时 Instance 还未被初始化。使⽤ volatile 可以禁⽌ JVM 的指令重排,保证在多线程环境下也能正常运⾏。 总结:如果不使用volatile关键词修饰,可能会导致拿到的对象是未被初始化的。

# 饿汉式 静态内部类

通过JVM来保证线程安全,高效

public class SingleTon {
    private SingleTon() {
    }
    private static class SingleTonHolder {
        private static final SingleTon INSTANCE = new SingleTon();
    }

    public static SingleTon getInstances() {
        return SingleTonHolder.INSTANCE;
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 枚举方式

public enum SingleTonEnum {
    /**
     * 实例
     */
    INSTANCE;

    public void doSomething() {
        //todo
    }
}
1
2
3
4
5
6
7
8
9
10

# 如何选用

  • 单例对象 占用资源少,不需要延时加载,枚举 好于 饿汉

  • 单例对象 占用资源多,需要延时加载,静态内部类 好于 懒汉式

在 GitHub 编辑此页 (opens new window)
上次更新: 2024/02/25, 12:11:11
设计模式-代理
设计模式-迭代器

← 设计模式-代理 设计模式-迭代器→

Theme by Vdoing | Copyright © 2018-2024 unclezs
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式