# 单例模式
单例模式有非常多种的实现方式,具体如下:
## 饿汉式 - 静态常量
~~~
package cn.net.smrobot.singleton.type1;
/**
* 饿汉式,使用静态变量的方式
*/
public class Singleton {
/**
* 将构造器私有化
*/
private Singleton() {}
/**
* 创建本地变量实例
*/
private final static Singleton instance = new Singleton();
/**
* 静态方法用来返回实例
* @return
*/
public static Singleton getInstance() {
return instance;
}
}
~~~
优点:
1. 无多线程问题,随着类的加载被加载
缺点:
1. 没有达到懒加载的效果,如果一直没有使用到这个实例,可能会导致内存的浪费。
如果确定该实例一定会被使用,则可以使用这个方式。
## 饿汉式 - 静态代码块
~~~
package cn.net.smrobot.singleton.type2;
/**
* 饿汉式 - 使用静态代码块
*/
public class Singleton {
public static void main(String[] args) {
}
/**
* 私有化构造方法
*/
private Singleton() {}
private static Singleton instance;
/**
* 静态代码块实例对象
*/
static {
instance = new Singleton();
}
public static Singleton getInstance() {
return instance;
}
}
~~~
效果与静态常量的方法是一样的。
## 懒汉式 - 线程不安全
~~~
package cn.net.smrobot.singleton.type3;
/**
* 懒汉式
* 线程不安全
*/
public class Singleton {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
System.out.println("hashCode = " + instance.hashCode());
System.out.println("hashCode = " + instance1.hashCode());
}
private static Singleton instance;
private Singleton() {}
/**
* 调用该方法的使用才去创建单例对象
*/
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
~~~
优点:
1. 可以起到懒加载的效果
缺点:
1. 在多线程的环境下,如果一个线程进入`if(instance == null)`语句判断,但是还没来得及执行`new instance()`,别的线程也进入了这个语句判断,这样可能会导致多个实例对象被创建,达不到单例的效果。
> 在实际开发中不要使用这种方式
## 懒汉式 - 线程安全
~~~
package cn.net.smrobot.singleton.type4;
/**
* 懒汉式
* 线程安全 - 使用同步方法
*/
public class Singleton {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
System.out.println("hashCode = " + instance.hashCode());
System.out.println("hashCode = " + instance1.hashCode());
}
private static Singleton instance;
private Singleton() {}
/**
* 调用该方法的使用才去创建单例对象
* 加入同步处理代码
* 解决线程安全问题
*/
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
~~~
这种方式使用`synchronized`同步的方式,但是在方法级别进行同步效率比较低,在实际开发中也不推荐使用。
## 懒汉式 - 同步代码块
~~~
package cn.net.smrobot.singleton.type5;
/**
* 懒汉式
* 线程安全 - 使用同步代码块
*/
public class Singleton {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
System.out.println("hashCode = " + instance.hashCode());
System.out.println("hashCode = " + instance1.hashCode());
}
private static Singleton instance;
private Singleton() {}
/**
* 调用该方法的使用才去创建单例对象
* 加入同步处理代码
* 解决线程安全问题
*/
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
~~~
仍然是线程不安全的,例如在进行if判断时候有多个线程进入该分支,就会导致对象被同步的创建多次。
**不要使用**
## 双重检查 - Double Check
推荐使用,可以解决线程安全,懒加载,效率问题。
~~~
package cn.net.smrobot.singleton.type6;
/**
* 双重检查
*/
public class Singleton {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
System.out.println("hashCode = " + instance.hashCode());
System.out.println("hashCode = " + instance1.hashCode());
}
/**
* volatile 关键字很重要
*/
private static volatile Singleton instance;
private Singleton() {}
/**
* 调用该方法的使用才去创建单例对象
* 使用两个if进行双重检查
*/
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
// 再进行一步判断
instance = new Singleton();
}
}
}
return instance;
}
}
~~~
## 静态内部类
~~~
package cn.net.smrobot.singleton.type7;
/**
* 静态内部类
*/
public class Singleton {
public static void main(String[] args) {
}
private Singleton() {}
/**
* 只有调用getInstance方法时静态内部类才会被加载
* 实例对象会被唯一创建一次
* 利用JVM底层机制类装载的时候是线程安全的
*/
private static class SingletonInstance {
private final static Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
~~~
可以达到线程安全和延迟加载的效果,线程安全是利用JVM的类装载机制,JVM可以保证在加载一个类的时候是线程安全的,推荐使用。
## 枚举类型
~~~
package cn.net.smrobot.singleton.type8;
/**
* 枚举模式
*/
public enum Singleton {
INSTANCE;
}
~~~
由于其他单例模式的实质都是将构造方法私有化,但是通过反射是可以将构造方法设置为共有化的,于是就可以创建更多的对象了。而反射的代码会判断该类型是不是枚举类型,如果是枚举类型则会直接抛出异常。同时枚举类型的序列化与反序列化可以保证对象一致。
可以避免多线程同步问题并且能够防止反序列化重新创建新的对象,推荐使用。
> 参考:https://www.cnblogs.com/chiclee/p/9097772.html
## JDK中的源码
RunTime
## 单例模式使用场景
1. 需要频繁的进行创建和销毁的对象。
2. 创建对象时耗时过多或者资源浪费过多。
3. 经常用到的对象,工具类对象,频繁访问数据库或文件的对象(数据源,session工厂)。
4. Spring IOC容器的ApplicationContext用的是饿汉式的单例模式。
- 第一章 Java基础
- ThreadLocal
- Java异常体系
- Java集合框架
- List接口及其实现类
- Queue接口及其实现类
- Set接口及其实现类
- Map接口及其实现类
- JDK1.8新特性
- Lambda表达式
- 常用函数式接口
- stream流
- 面试
- 第二章 Java虚拟机
- 第一节、运行时数据区
- 第二节、垃圾回收
- 第三节、类加载机制
- 第四节、类文件与字节码指令
- 第五节、语法糖
- 第六节、运行期优化
- 面试常见问题
- 第三章 并发编程
- 第一节、Java中的线程
- 第二节、Java中的锁
- 第三节、线程池
- 第四节、并发工具类
- AQS
- 第四章 网络编程
- WebSocket协议
- Netty
- Netty入门
- Netty-自定义协议
- 面试题
- IO
- 网络IO模型
- 第五章 操作系统
- IO
- 文件系统的相关概念
- Java几种文件读写方式性能对比
- Socket
- 内存管理
- 进程、线程、协程
- IO模型的演化过程
- 第六章 计算机网络
- 第七章 消息队列
- RabbitMQ
- 第八章 开发框架
- Spring
- Spring事务
- Spring MVC
- Spring Boot
- Mybatis
- Mybatis-Plus
- Shiro
- 第九章 数据库
- Mysql
- Mysql中的索引
- Mysql中的锁
- 面试常见问题
- Mysql中的日志
- InnoDB存储引擎
- 事务
- Redis
- redis的数据类型
- redis数据结构
- Redis主从复制
- 哨兵模式
- 面试题
- Spring Boot整合Lettuce+Redisson实现布隆过滤器
- 集群
- Redis网络IO模型
- 第十章 设计模式
- 设计模式-七大原则
- 设计模式-单例模式
- 设计模式-备忘录模式
- 设计模式-原型模式
- 设计模式-责任链模式
- 设计模式-过滤模式
- 设计模式-观察者模式
- 设计模式-工厂方法模式
- 设计模式-抽象工厂模式
- 设计模式-代理模式
- 第十一章 后端开发常用工具、库
- Docker
- Docker安装Mysql
- 第十二章 中间件
- ZooKeeper