设计模式-单例模式

单例模式模式一般大家都会认为它比较简单,其实并非你们所认为的那样,很多情况下,单例模式会涉及到很多优化,下面给大家简单介绍一下单例模式的几种演变过程:

  • 饿汉模式
  • 懒汉模式 
  • 懒汉模式(加锁关键字 synchronized)
  • 懒汉模式(细粒度的添加synchronized)
  • 懒汉模式(双重检查)
  • 静态内部类
  • 枚举类

第一种:饿汉模式  比较简单,类加载到内存的时候就进行实例化,推荐使用 ,但是有人会说,既然不用干嘛要进行实例化;

package com.dongl.singleton;

/**
 * 饿汉模式
 * 类加载到内存就直接实例化一个单例,JVM会保证它的线程安全
 * 简单使用 推荐使用
 * 缺点:无论用到与否 类加载就会直接实例化
 * 有人就会吹毛求疵说:你不用 你实例化干嘛?
 */
public class T01_Singleton {
    private static T01_Singleton INSTANCE = new T01_Singleton();

    public T01_Singleton() {
    }

    public static T01_Singleton  getInstance(){
        return INSTANCE;
    }

    public static void main(String[] args) {
        T01_Singleton t1 = T01_Singleton.getInstance();
        T01_Singleton t2 = T01_Singleton.getInstance();
        System.out.println(t1 == t2);
    }
}

第二种:懒汉模式 这种模式在多线程的情况下会出现问题

package com.dongl.singleton;


/**
 * 懒汉模式 lazy loading
 * 虽然达到了按需初始化的目的 但是也带来了线程安全的问题
 * 在多线程的情况下
 */
public class T02_Singleton {
    private static T02_Singleton INSTANCE = null;

    public T02_Singleton() {
    }

    public static T02_Singleton getInstance(){
        if(INSTANCE == null){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE =  new T02_Singleton();
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
//        T02_Singleton t1 = T02_Singleton.getInstance();
//        T02_Singleton t2 = T02_Singleton.getInstance();
//        System.out.println(t1 == t2);

        /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(T02_Singleton.getInstance().hashCode());
            }).start();
        }
    }
}

//运行结果 hashcode值不相同

E:JDKjdk1.8injava.exe "-javaagent:E:IdeaIntelliJ IDEA
1365279759
1365279759
1365279759
1945255694
226994615


第三种:懒汉模式(加锁关键字 synchronized)这种方式解决了懒汉模式下多线程问题,但是同时带来的问题是效率降低;

package com.dongl.singleton;


/**
 * 懒汉模式 lazy loading
 * 虽然达到了按需初始化的目的 但是也带来了线程安全的问题
 * 解决办法使用synchronized 但是带来的问题就是效率下降
 */
public class T03_Singleton {
    private static T03_Singleton INSTANCE = null;

    public T03_Singleton() {
    }

    /**
     * 加锁的方式有两种 一种是对方法进行加锁 另一种是对代码块进行加锁
     * 涉及到的无非是锁的粒度问题
     * @return
     */
    public static /**synchronized*/ T03_Singleton getInstance(){
        synchronized (T02_Singleton.class) {
            if (INSTANCE == null) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new T03_Singleton();
            }
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(T03_Singleton.getInstance().hashCode());
            }).start();
        }
    }
}

第四种:懒汉模式(细粒度的添加synchronized)试图通过减小锁的粒度来进行改善效率的问题 但是不可行;

package com.dongl.singleton;


/**
 * 懒汉模式 lazy loading
 * 虽然达到了按需初始化的目的 但是也带来了线程安全的问题
 * 解决办法使用synchronized 但是带来的问题就是效率下降
 *
 * 试图通过减小锁的粒度来进行改善效率的问题  但是不可行
 */
public class T04_Singleton {
    private static T04_Singleton INSTANCE = null;

    public T04_Singleton() {
    }

    public static /**synchronized*/ T04_Singleton getInstance(){
        if (INSTANCE == null) {
            //试图通过减小锁的粒度来进行改善效率的问题  但是不可行
            synchronized (T02_Singleton.class) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new T04_Singleton();
            }
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(T04_Singleton.getInstance().hashCode());
            }).start();
        }
    }
}

第五种:懒汉模式(双重检查)这种方式解决了多线程带来的问题;

package com.dongl.singleton;


/**
 * 懒汉模式 lazy loading
 * 虽然达到了按需初始化的目的 但是也带来了线程安全的问题
 * 解决办法使用synchronized 但是带来的问题就是效率下降
 * 因为锁的粒度很小也会带来多线程问题
 * 这时可以使用双重检查 来避免
 */
public class T05_Singleton {
    private volatile static T05_Singleton INSTANCE = null;

    public T05_Singleton() {
    }

    public static /**synchronized*/
    T05_Singleton getInstance(){
        if (INSTANCE == null) {
            synchronized (T02_Singleton.class) {
                //双重检查
                if(INSTANCE == null) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new T05_Singleton();
                }
            }
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(T05_Singleton.getInstance().hashCode());
            }).start();
        }
    }
}

第六种:静态内部类实现懒加载(lazy loading)这种方式是最优解之一  因为在加载类的时候 不加载内部类这样就实现了懒加载;

package com.dongl.singleton;


/**
 * 静态内部类的法方法
 * JVM保证单例
 * 加载外部类的时候 不会加载内部类 这样实现了懒加载
 */
public class T06_Singleton {

    public T06_Singleton() {
    }

    //静态内部类
    private static class T06_SingletonHandler{
        private final static T06_Singleton INSTANCE = new T06_Singleton();
    }

    public static T06_Singleton getInstance(){
        T06_Singleton instance = T06_SingletonHandler.INSTANCE;
        return instance;
    }

    public static void main(String[] args) {
        /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(T06_Singleton.getInstance().hashCode());
            }).start();
        }
    }
}

第七种:枚举类  不仅可以解决线程同步,还可以防止反序列化。

package com.dongl.singleton;


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

    INSTANCE;

    public static void main(String[] args) {
        /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(T07_Singleton.INSTANCE.hashCode());
            }).start();
        }
    }
}

以上其中方式是按照问题出现 一步步的优化得到的,如果你觉得有疑问可以评论区说出你的观点,一起讨论一起进步!!!

原文地址:https://www.cnblogs.com/dongl961230/p/13298631.html