前言
熬过了前面的那些磕磕绊绊,我们正式进入到java的精髓,设计模式的讲解,本章来尝鲜单例设计模式
范例
1.单例设计模式的作用
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
- 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
- 单例模式可以在系统设置全局的访问点,优化共享资源访问。例如:可以设计单例类来负责所有数据表的映射处理
2.常见的单例模式设计方法
- 饿汉式(线程安全、调用效率高,但不能延时加载)
package com.jwang.test;
public class RegexTest {
public static void main(String[] args) {
System.out.println(Singleton.getInstance()==Singleton.getInstance());
}
}
//饿汉式实现单例
class Singleton{
private static Singleton singleton = new Singleton();//类初始化立刻加载类对象
private Singleton(){}//构造器私有化
//供外部获取该类实例
public static Singleton getInstance(){
return singleton;
}
}
- 懒汉式(线程安全、调用效率不高,但是可以延时加载)
package com.jwang.test;
public class RegexTest {
public static void main(String[] args) {
System.out.println(Singleton.getInstance()==Singleton.getInstance());
}
}
//懒汉式实现单例
class Singleton{
private static Singleton singleton;
private Singleton(){}//构造器私有化
//供外部获取该类实例
public static synchronized Singleton getInstance(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
以上是常见的两种单例模式的实现方式!
通过分析可以得出结论:上面两种单例的实现方式各有优缺点,下面介绍几种其他的单例实现优化上面的实现方式!
- 双重检测锁实现(不建议使用)
我们知道,懒汉式的实现方式是在获取实例的时候,获取实例的方法加上了synchronized关键字来确保线程安全!但是带来的问题却是执行效率的低下!
双重检测锁模式将同步的功能放入方法的实现当中,只需要第一次同步创建对象之后就无需同步了,大大的提高了执行效率,又实现了懒加载!
通用实现代码如下:
public class SingletonDemo03 {
private static SingletonDemo03 instance = null;
public static SingletonDemo03 getInstance() {
if (instance == null) {
SingletonDemo03 sc;
synchronized (SingletonDemo0.class) {
sc = instance;
if (sc == null) {
synchronized (SingletonDemo03.class) {
if (sc == null) {
sc = new SingletonDemo03();
}
}
instance = sc;
}
}
}
return instance;
}
private SingletonDemo03() {
}
}
由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题。不建议使用!
- 静态内部类实现 (常用,稳定)
由于双重检测锁实现方式存在诸多的问题,这里提出了静态内部类的实现方法,更好的兼顾懒汉和饿汉的优点!
实现代码如下:
public class SingletonDemo04 {
private static class SingletonClassInstance {
private static final SingletonDemo04 instance = new SingletonDemo04();
}
public static SingletonDemo04 getInstance() {
return SingletonClassInstance.instance;
}
private SingletonDemo04() {
}
}
要点:
- 外部类没有static属性,则不会像饿汉式那样立即加载对象。
- 只有真正调用getInstance(),才会加载静态内部类。加载类时是线程安全的。
- instance是static final类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性.
- 兼备了并发高效调用和延迟加载的优势!
- 枚举方式实现
该种实现比较容易,利用了枚举类型本身的单例化特性!
public enum SingletonDemo05 {
INSTANCE;
public void play(){
//other operation
}
public static void main(String[] args) {
SingletonDemo05 singletonDemo05 = SingletonDemo05.INSTANCE;
System.out.println(singletonDemo05);
}
}
优点:
– 实现简单
– 枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!
缺点:
– 无延迟加载
总结:
单例模式有五种实现方式,你除了要知道懒汉和饿汉,你还要知道其余其他实现方式,面试会大大加分!
- 饿汉实现方式:实例化对象耗费资源不大且无需懒加载可以使用,执行效率高
- 懒汉实现方式:需要懒加载或实例化对象消耗资源大可以使用,但执行效率低
- 双重检测锁实现方式:由于内部原因,不推荐使用,了解即可
- 静态内部类实现方式:如果需要懒加载又要考虑执行效率,选择该种实现方式再好不过啦
- 枚举实现方式:如果不需要懒加载,优先选用