设计模式系列文章目录
如果本文对你们的开发之路有所帮助,请帮忙点个赞,您的支持是我坚持写博客的动力
【设计模式相关书籍】wx关注【Java从零学架构】,后 台回复【设计模式】自取
前言
上一篇文章带着大家输入学习了设计模式的工厂模式,这篇文章带着大家深入单例模式
项目代码见 https://gitee.com/janyxe/design_patterns
什么是单例模式
-
保证一个类只有一个实例,并且提供一个全局访问点
-
当前JVM中只会有一个实例对象
能画出单例模式类图吗?并简单说明下
单例模式
保证一个类只有一个实例
说说单例模式的应用场景
- Servlet对象 默认为单例
- 多线程的线程池的设计采用单例模式
- Spring中Bean对象默认为单例模式
- 定义枚举常量信息
- 项目配置文件为单例模式
单例模式有哪些创建方式?
恶汉式
、懒汉式
、饿汉式(线程安全)
、双重检验锁
、枚举
、内部类
、静态类
懒汉模式
概念
延迟加载,只有真正使用的时候,才开始实例化
懒汉模式实现
线程不安全版本
代码见
cn.jany.singleton.slacker.SlackerSingleton
/**
* 懒汉模式线程不安全实现
*/
public class SlackerSingleton {
private static SlackerSingleton slackerSingleton = null;
private SlackerSingleton(){
}
/**
* 获取实例方法
* @return
*/
public static SlackerSingleton getInstance(){
if (slackerSingleton == null){
// 当获取实例为null时创建实例
slackerSingleton = new SlackerSingleton();
}
return slackerSingleton;
}
public static void main(String[] args) throws InterruptedException {
SlackerSingleton slackerSingleton = SlackerSingleton.getInstance();
SlackerSingleton slackerSingleton2 = SlackerSingleton.getInstance();
// 控制台输出true,表示获取的实例为同一个
System.out.println(slackerSingleton == slackerSingleton2);
}
}
控制台输出true,代表两个对象一致
在多线程环境中存在多个对象 ,线程不安全
代码见
cn.jany.singleton.slacker.SlackerSingleton1
public class SlackerSingleton1 {
public static void main(String[] args){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
SlackerSingleton slackerSingleton = SlackerSingleton.getInstance();
System.out.println(slackerSingleton);
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
SlackerSingleton slackerSingleton = SlackerSingleton.getInstance();
System.out.println(slackerSingleton);
}
});
// 创建2个线程
thread.start();
thread2.start();
}
}
输出对象不同
cn.jany.singleton.slacker.SlackerSingleton@4909e27e
cn.jany.singleton.slacker.SlackerSingleton@593fdda8
线程安全版本
通过synchronized
关键字可以保证线程安全,不过效率相对较低
代码见
cn.jany.singleton.slacker.SlackerSingleton2
public class SlackerSingleton2 {
private static SlackerSingleton2 slackerSingleton2 = null;
public static synchronized SlackerSingleton2 getInstance(){
if (slackerSingleton2 == null){
slackerSingleton2 = new SlackerSingleton2();
}
return slackerSingleton2;
}
public static void main(String[] args) {
SlackerSingleton2 instance = SlackerSingleton2.getInstance();
SlackerSingleton2 instance1 = SlackerSingleton2.getInstance();
System.out.println(instance == instance1);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(SlackerSingleton2.getInstance());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(SlackerSingleton2.getInstance());
}
}).start();
}
}
懒汉式双重检验锁
通过双重检验锁加锁优化
编译器(JIT),CPU 有可能对指令进行重排序
类加载过程:分配空间、初始化、引用赋值
重排序之后可能的结果:分配空间、引用赋值(多线程其他线程获取实例可能为未初始化的实例)、初始化
通过volatile
关键字进行修饰可以防止重排序
代码见
cn.jany.singleton.slacker.SlackerSingleton3
public class SlackerSingleton3 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
}
}
class Singleton{
/**
* volatile 防止重排序
*/
private volatile static Singleton singleton = null;
/**
* 构造函数
*/
private Singleton(){
}
/**
* 获取实例
* @return
*/
public static Singleton getInstance(){
// 第一层校验
if (singleton == null){
synchronized (Singleton.class){
// 第二层校验
if (singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
恶汉模式
概念
-
类加载的 初始化阶段就完成了实例的初始化。
-
通过jvm加载机制保证实例
唯一性(初始化只会执行一次)
和线程安全(JVM 以同步
方式完成类加载过程)
类加载过程会经过:
加载
、验证
、准备
、解析
、初始化
、使用
、卸载
类加载过程各阶段解析:
- 加载:在硬盘查找并通过IO读取字节码文件,在加载节点生成这个类的java.class.Class对象
- 验证:校验字节码文件的准确性
- 解析:讲符号引用替换为直接引用
- 初始化:对类的静态变量初始化为指定的值,执行
静态代码块
只有在真正使用对应的类时,才会触发初始化
恶汉模式实现
恶汉模式实现方式一
代码见
cn.jany.singleton.villain.VillainSingleton
/**
* 恶汉模式
*/
public class VillainSingleton {
public static final VillainSingleton villainSingleton = new VillainSingleton();
/**
* 构造方法
*/
private VillainSingleton(){
}
/**
* 获取实例
* @return
*/
private static VillainSingleton getInstance(){
return villainSingleton;
}
public static void main(String[] args) {
VillainSingleton instance = VillainSingleton.getInstance();
VillainSingleton instance2 = VillainSingleton.getInstance();
System.out.println(instance == instance2);
}
}
控制台输出
true
恶汉模式实现方式二
代码见
cn.jany.singleton.villain.VillainSingleton2
/**
* 恶汉模式
*/
public class VillainSingleton2 {
public static final VillainSingleton2 singleton = new VillainSingleton2();
/**
* 构造函数
*/
private VillainSingleton2(){
}
public static void main(String[] args) {
VillainSingleton2 singleton = VillainSingleton2.singleton;
VillainSingleton2 singleton1 = VillainSingleton2.singleton;
System.out.println(singleton == singleton1);
}
}
控制台输出
true
静态内部类
概念
- 利用类的加载机制保证线程安全
- 只有在实际使用的时候,才会触发类的初始化
静态内部类实现
代码见
cn.jany.singleton.inner.InnerClassSingletonTest
public class InnerClassSingletonTest {
public static void main(String[] args) {
InnerClassSingleton instance = InnerClassSingleton.getInstance();
InnerClassSingleton instance2 = InnerClassSingleton.getInstance();
System.out.println(instance == instance2);
}
}
class InnerClassSingleton{
static {
System.out.println("InnerClassSingleton static ");
}
private InnerClassSingleton(){
}
public static InnerClassSingleton getInstance() {
return SingletonHolder.instance;
}
private static class SingletonHolder {
private static InnerClassSingleton instance= new InnerClassSingleton();
static {
System.out.println( "SingletonHolder static" );
}
}
}
控制台输出
InnerClassSingleton static
SingletonHolder static
true
静态代码块
静态代码块实现
代码见
cn.jany.singleton.inner.StaticSingleton
/**
* 静态代码块
*/
public class StaticSingleton {
private static StaticSingleton staticSingleton;
static {
staticSingleton = new StaticSingleton();
}
public static StaticSingleton getInstance(){
return staticSingleton;
}
public static void main(String[] args) {
StaticSingleton instance = StaticSingleton.getInstance();
StaticSingleton instance1 = StaticSingleton.getInstance();
System.out.println(instance == instance1);
}
}
控制台输出
true
枚举实现单例
代码见
cn.jany.singleton.enums.EnumSingletonTest
枚举实现单例实现
/**
* 枚举实现单例模式
*/
public enum EnumSingleton {
INSTANCE;
}
public class EnumSingletonTest {
public static void main(String[] args) {
EnumSingleton instance = EnumSingleton.INSTANCE;
EnumSingleton instance1 = EnumSingleton.INSTANCE;
System.out.println(instance == instance1);
}
}
控制台输出
true