设计模式--单例模式,小小单例,有点儿东西~六种写法,理解透彻

单例模式

noob网站的介绍:
地址:http://www.runoob.com/design-pattern/singleton-pattern.html 0.0
这里写图片描述

探探原理

饿汉模式:

//饿汉式
public class Demo1 {
    //1.私有的构造方法
    //2.返回实例的值
    //3.类中自己创建实例
    private static Demo1 instance = new Demo1();

    private Demo1() {
    }

    public static Demo1 getInstance() {
        return instance;
    }

    public void doSomeThing() {
        System.out.println("单例单例");
    }
}

说明:

  • 为什么代码要这么写啊?
    • 构造器私有:单例单例,只能有一个实例,别人不能new你这个类,所以构造器写成private私有的,这样只能你自己使用.
    • 这个类的作用是什么?为别人提供一个对象使用,所以getInstance负责给别人一个对象
    • 这个对象来自何处?构造器只能自己用,只能自己创建了咯~
    • 这个对象提供给别人,干什么呢?doSomeThing实例方法给这个实例调用
    • 为什么叫饿汉啊?不管三七二十一,只要类被初始化,我就new一个对象进内存里面.PS,你知道类加载的时候,静态域(静态变量)会被初始化不?
  • 线程安全不?安全,因为类加载只有一个线程干啊
  • 缺点:如果这个对象比较大,比价复杂,不管三七二十一就往内存里面扔一个对象,那势必造成占用吧?

首先你要理解这段代码,因为这是单例模式最常用的一种.让我们接着往下看

懒汉模式:

// 懒汉式-非线程安全
class Demo2 {
    private static Demo2 instace;

    private Demo2() {

    }

    private static Demo2 getInstace() {
        if (instace == null) {
            instace = new Demo2();
        }
        return instace;
    }

    public void doSomeThing() {
        System.out.println("单例单例");
    }
}

注意:

  • 为什么懒? 它在类加载的时候不会立刻new一个对象进内存,而是先看看这个对象是不是已经被new出来了
  • 线程安全吗?不安全,当两个线程同时getInstace时候,都判断instace是空,都new 了一个,这就不是单例模式了把?变成双例模式了,就是这个道理

懒汉-安全:

// 懒汉式-线程安全
class Demo3 {
    private static Demo3 instance;

    private Demo3() {
    }

    public synchronized static Demo3 getInstance() {
        if (instance == null) {
            instance = new Demo3();
        }
        return instance;
    }

    public void doSomeThing() {
        System.out.println("单例单例");
    }
}

添加了类级同步锁,访问变成同步的,但是效率比较低,多个线程调用getInstance的时候一定会阻塞.这个时候DCL登场

DCL:双重检测 double-checked locking:

// DCL -双重检测
class Demo4 {
    private volatile static Demo4 instance;

    private Demo4() {

    }

    private static Demo4 getInstance() {
        if (instance == null) {
            synchronized (Demo4.class) {
                if (instance == null) {
                    instance = new Demo4();
                }
            }
        }
        return instance;
    }

    public void doSomeThing() {
        System.out.println("单例单例");
    }
}

DCL设计出来就是为了在懒汉模式下,继续提高性能

  • volatile关键字,这个关键字保证3个特性,可见性,有序性,原子性.这里主要是防止指令重排序,(新入门的同学可能不懂这个,建议搜搜,值得一学,是JVM原理)http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization,这个链接讲的很细致了,可以进去看看.
  • 两次检查提高了性能,把第一次检查,放在同步代码块外面,只要有了第一个new,之后的就不需要进入同步代码块了
    PS:DCL双重检查,说实话,挺复杂的,真正用这个,感觉不是太好,noob最后的经验之谈也说明了这个问题.

静态代码块:

// 登记,静态内部类
class Demo5 {
    private static class Holder

    {
        private static final Demo5 INSTACE = new Demo5();
    }

    private Demo5() {

    }

    private static Demo5 getInstace() {
        return Holder.INSTACE;
    }

    public void doSomeThing() {
        System.out.println("单例单例");
    }
}
  • 线程安全,使用的是类加载的原理
  • 属于懒汉模式,因为只用调用getInstace()的时候,才会加载静态内部类,进行静态变量的初始化

枚举:

enum Demo6 {
    SINGLE;

    public void doSomeThing() {
        System.out.println("单例单例");
    }
}

小小枚举,代码很少,含知识量高~参见下面的这个链接,讲的很好,挖的都是底层,也是利用类加载的机制,实现Demo6的单例化,
http://www.hollischuang.com/archives/197

总结

小小单例,6种方法,饿汉常用,懒汉12性能太低,不可能被用,DCL太复杂,若是要延迟找静态,枚举类完爆一切zzz写法简单,功能强大

猜你喜欢

转载自blog.csdn.net/qq_41376740/article/details/81089443