设计模式1 一文搞懂单例模式

单例模式

单例模式就是保证我们使用到的都是同一个对象,主要有四种

  1. 饿汉式
  2. 懒汉式
  3. 静态内部类方式
  4. 枚举方式

饿汉式

package com.hejiale.singleton;

/**
 * 饿汉式:
 * 类加载到内存中,就会实例化一个单例,从而保证线程安全
 * 简单实用,推荐使用
 * 唯一缺点:就是不论用到与否,在类加载的时候,就会实例化对象 Class.forName("")
 */
public class Mgr01 {
    private static final Mgr01 INSTANCE = new Mgr01();
    //构造方法设置为private,我们就无法使用new来实例化对象
    private Mgr01(){}
    public static Mgr01 getInstance(){
        return INSTANCE;
    }

    public static void main(String[] args) {
        Mgr01 m1=Mgr01.getInstance();
        Mgr01 m2=Mgr01.getInstance();
        System.out.println(m1==m2);
    }
}

懒汉式

了解饿汉式,先要了解一下同步synchronized,点击这个网页了解https://www.cnblogs.com/three-fighter/p/14396208.html

package com.hejiale.singleton;

/**
 * lazy loading
 * 也称懒汉式
 * 虽然达到了按需初始化的目的(也就是用的时候,才初始化),但却带来线程不安全的问题-->Mgr03解决
 */
public class Mgr02 {
    private static Mgr02 INSTANCE;

    private Mgr02() {
    }
    /*
        所谓线程不安全的问题就是,比如说:
            一号线程,执行到if (INSTANCE == null) ,判断当前还没有
            实例化对象,然后准备执行之后的INSTANCE = new Mgr02();
            二号线程,执行到if (INSTANCE == null) ,判断当前还没有
            实例化对象,然后准备执行之后的INSTANCE = new Mgr02();
        那么当前情况下,INSTANCE在两个线程里,就不是同一个对象了
     */
    public static Mgr02 getInstance() {
        if (INSTANCE == null) {
            try {
                //每个线程执行到这里睡眠1毫秒
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Mgr02();
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(Mgr02.getInstance().hashCode());
            }).start();
        }
    }
}
package com.hejiale.singleton;

/**
 * lazy loading的改进
 * 可以通过synchronized解决,但也会带来效率下降
 */
public class Mgr03 {
    private static Mgr03 INSTANCE;

    private Mgr03() {
    }
    public static synchronized Mgr03 getInstance() {
        if (INSTANCE == null) {
            try {
                //每个线程执行到这里睡眠1毫秒
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Mgr03();
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(Mgr03.getInstance().hashCode());
            }).start();
        }
    }
}
package com.hejiale.singleton;

/**
 * 在线程安全懒汉式的基础上加快效率,完美写法
 */
public class Mgr04 {
    private static Mgr04 INSTANCE;

    private Mgr04() {
    }
    public static Mgr04 getInstance() {
        if (INSTANCE == null) {
            //双重检查
            synchronized (Mgr04.class){
                if (INSTANCE == null){
                    try {
                        //每个线程执行到这里睡眠1毫秒
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Mgr04();
                }
            }
        }
        return INSTANCE;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(Mgr04.getInstance().hashCode());
            }).start();
        }
    }
}

静态内部类方式

这里首先要了解静态内部类的特点,点击这个网页http://c.biancheng.net/view/1026.html

package com.hejiale.singleton;

/**
 * 静态内部类方式
 * JVM保证单例
 * 加载外部类的时候不会加载内部类,这样子可以实现懒加载
 */
public class Mgr05 {
    private Mgr05() {
    }

    public static class Mgr05Inner {
        private static final Mgr05 INSTANCE = new Mgr05();
    }

    public static Mgr05 getInstance() {
        return Mgr05Inner.INSTANCE;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(Mgr05.getInstance().hashCode());
            }).start();
        }
    }
}

枚举方式

package com.hejiale.singleton;

import java.lang.management.ThreadInfo;

/**
 * 不仅可以解决线程同步,还可以防止反序列化
 */
public enum Mgr06 {
    INSTANCE;

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(Mgr06.INSTANCE.hashCode());
            }).start();
        }
    }
}

总结

一共四种方式中,我们一般最经常使用的就是饿汉式,但是如果追求完美,则推荐使用枚举的方式

你可能感兴趣的:(java设计模式)