设计模式-单例模式(一)-创新互联
- 一 官方定义
- 二 单例模式八种方式
- 2.1 饿汉式(静态常量)
- 代码案例
- 案例分析
- 2.2 饿汉式(静态代码块)
- 代码案例
- 案例分析
- 2.3 懒汉式(线程不安全)
- 代码案例
- 案例分析
- 2.4 懒汉式(线程安全,同步方法)
- 代码案例
- 案例分析
- 2.5 懒汉式(线程不安全,同步代码块)
- 代码案例
- 案例分析
- 2.6 双重检查 (推荐使用)
- 代码案例
- 案例分析
- 优点
- 可能出现的问题
- 扩展 - Volatile
- 2.7 静态内部类 (推荐使用)
- 代码案例
- 案例分析
- 2.8 枚举方式
- 代码案例
- 案例分析
- 三 注意事项
- 四 单例模式的使用场景
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)
Spring 中的 bean 默认都是单例模式,每个bean定义只生成一个对象实例,每次 getBean请求获得的都是此实例
二 单例模式八种方式单例模式的八种实现方式,如下所示
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举方式
class Singleton {//一:构造器的私有化 防止外部用构造器...
private Singleton() {}
//二:类的内部创建对象 final static
private static final Singleton singleton = new Singleton();
//三:对外提供公共的静态方法 返回该类唯一的对象实例
public static Singleton getInstance() {return singleton;
}
}
案例分析//案例演示 - 饿汉式
public class SingletonDemo {public static void main(String[] args) {// 方式一:静态常量
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1); //true
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
通过结果可以发现返回的是同一个对象所以单例模式是实现的。
写法分析优势
: 简单 避免多线程的同步问题劣势
: 没有达到懒加载的效果 内存的浪费
//方式二:静态代码块的方式
class Singleton {//构造器私有化
private Singleton() {}
//类的内部创建对象
private static final Singleton singleton;
static {singleton = new Singleton();
}
//对外提供公共的静态的方法
public static Singleton getInstance() {return singleton;
}
}
案例分析优势
: 简单 避免多线程的同步问题劣势
: 没有达到懒加载的效果 内存的浪费
class Singleton{//构造器私有化
private Singleton(){}
//类的内部提供对象
private static Singleton singleton;
//对外提供公共的静态方法的时候,来判断
public static Singleton getInstance(){if (singleton == null){singleton = new Singleton();
}
return singleton;
}
}
案例分析优势
:起到了懒加载的效果 不会造成内存浪费
在使用到的时候才会创建对象。判断有无对象,有则返回,无创建后再返回
劣势
:只能在单线程下使用,多线程情况下线程不安全 不推荐这种方式的
2.4 懒汉式(线程安全,同步方法)① 在多线程的情况下,有一个对象进入if判断通过,还没执行到创建对象这一步骤时
② 有另外一个对象也进入了if判断,也通过了。
此时就会出现多个实例,造成线程不安全。
在获取对象的静态方法上添加 synchronized 关键字,实现同步方法。解决线程不安全问题。
代码案例//加入同步处理 同步方法
class Singleton{//构造器私有化
private Singleton(){}
//类的内部提供对象
private static Singleton singleton;
//对外提供公共的静态方法的时候,来判断
public static synchronized Singleton getInstance(){if (singleton == null){singleton = new Singleton();
}
return singleton;
}
}
案例分析解决了线程安全问题,但是效率太低
2.5 懒汉式(线程不安全,同步代码块)每一个线程需求拿实例的时候都要在外面等候另一线程处理完
将实例化对象过程放入同步代码块中
代码案例//加入同步处理 - 同步代码块的方式 不推荐的
class Singleton{//构造器私有化
private Singleton(){}
//类的内部提供对象
private static Singleton singleton;
//对外提供公共的静态方法的时候,来判断
public static Singleton getInstance(){if (singleton == null){synchronized (Singleton.class){singleton = new Singleton();
}
}
return singleton;
}
}
案例分析不推荐的,解决不了线程的安全问题
2.6 双重检查 (推荐使用) 代码案例这种方式本意是想解决同步方法的问题。但是我们分析一下。在多线程情况下还是有可能创建多个实例的问题。
class Singleton{private Singleton(){}
//禁止指令重排
private static volatile Singleton singleton;
//加入双重检查机制
public static Singleton getInstance(){if (singleton == null){synchronized (Singleton.class){if (singleton == null){singleton = new Singleton();
}
}
}
return singleton;
}
}
案例分析
优点线程安全
解决了线程安全问题
① 多线程时,若两个线程同时满足第一层非空判断,等待调用同步代码块。
② 由于添加了synchronized ,当第一个线程进来时,发现Singleton对象为空,进行创建对象并返回。
③ 当另外一个线程调用同步代码块时,进行再次对象非空判断,发现对象已经创建成功。不在执行创建对象流程,返回已有对象
懒加载
使用到对象时才创建,不会造成内存的浪费
效率很高
可能出现的问题先进性了singleton 是否为空的判断,singleton 如果不为空直接返回结果。倘若无第一层判空,多线程时每次都要进行synchronized 等待其他线程处理结束,才能进入内部非空判断,效率相对低。
我们认为的 new Singleton() 操作
1)分配内存地址 M
2)在内存 M 上初始化Singleton 对象
3)将M的地址赋值给 instance 对象
JVM编译优化后(指令重排)可能的 new Singleton() 操作
1)分配内存地址 M
2)将M的地址赋值给instance变量
3)在内存M上初始化 Singleton 对象
这就有可能出现空指针异常
异常发生过程
解决方式:关键字 Volatile 来禁止指令重排
轻量级的同步机制 (低配版) 没有保证原子性
三大特性
保证可见性
其中一个线程修改了主内存共享变量的值,要写回主内存,并要及时通知其他线程可见
没有保证原子性
没法(不能保证)不可分割,完整,要么同时成功,要么同时失败
禁止指令重排
和底层内存屏障相关 避免多线程下出现指令乱序的情况
扩展-线程切换
Java的一条语句对应的cpu指令可能是多条,其中任意一条cpu指令在执行完都可能发生线程切换
count += 1,对应cpu 指令如下:
2.7 静态内部类 (推荐使用) 代码案例1)将变量count从内存加载到cpu寄存器
2)寄存器中 +1
3)将结果写入内存(缓存机制写入的可能是cpu而不是内存)
class Singleton{private Singleton(){}
private static class SingletonInstance{public static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){return SingletonInstance.INSTANCE;
}
}
案例分析①当Singleton类加载时,静态内部类是不会加载的。
②只有调用getInstance方法用到了SingletonInstance.INSTANCE静态变量时导致SingletonInstance静态内部类进行加载。
不会出现线程安全问题
JVM来帮我们保证了线程的安全性
利用静态内部类的特点,效率也很高,实际开发中推荐使用的
public class EnumDemo {public static void main(String[] args) {//验证其正确性
Singleton instance = Singleton.INSTANCE;
Singleton instance1 = Singleton.INSTANCE;
System.out.println(instance == instance1); //true
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}
enum Singleton{INSTANCE; //属性
}
案例分析不仅可以避免线程安全问题 还可以防止反序列化重新创建对象。推荐使用
三 注意事项- 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
- 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new
- 对于一些需要频繁创建销毁的对象
- 重量级的对象(创建对象时耗时过多,或者耗费资源过多)
- 经常使用到的对象
- 工具类对象
- 数据源,session。。。。
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
网站栏目:设计模式-单例模式(一)-创新互联
URL标题:http://hbruida.cn/article/djdeec.html