设计模式——单例模式

1 懒汉模式

此种最简单、方便,缺点可以忽略,建议使用

package com.xin.demo.sigle;

/**
 * 懒汉模式,简单实用,推荐使用这种写法
 * 类加载到内存后就实例化一个对象,jvm保证线程的安全
 * 缺点:不管是否使用,类加载时就进行实例化操作
 */
public class Single01 {

    /**
     * 类加载时进行对象的创建,jvm 保证类线程安全
     */
    private static final Single01 INSTANCE = new Single01();

    /**
     * 构造器必须私有化,不允许外部进行对象的创建
     */
    private Single01() {
    }

    public static Single01 getINSTANCE() {
        return INSTANCE;
    }

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

2 同1 静态代码块的写法

package com.xin.demo.sigle;

/**
 * 同01
 */
public class Single02 {

    /**
     * 类加载时进行对象的创建,jvm 保证类线程安全
     */
    private static final Single02 INSTANCE;

    // 同01 只是改成了 静态代码块
    static {
        INSTANCE = new Single02();
    }

    /**
     * 构造器必须私有化,不允许外部进行对象的创建
     */
    private Single02() {
    }

    public static Single02 getINSTANCE() {
        return INSTANCE;
    }

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

3 饿汉模式(非线程安全)

按需分配,此方法存在线程安全问题

package com.xin.demo.sigle;

/**
 * 饿汉模式,按需分配
 */
public class Single03 {

    private static Single03 INSTANCE = null;

    /**
     * 构造器必须私有化,不允许外部进行对象的创建
     */
    private Single03() {
    }

    public static Single03 getINSTANCE() {
        if (INSTANCE == null) {
            // 此处验证多线程并发的问题,虽然此处达到了 按需分配,但是多线程时不安全
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Single03();
        }
        return INSTANCE;
    }

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

4 饿汉模式(线程安全)

此处采用给方法上加 synchronized 关键字解决了线程安全问题,同时带来了性能降低的问题,每次获取实例时都会进行等待。

package com.xin.demo.sigle;

/**
 * 饿汉模式,按需分配
 */
public class Single04 {

    private static Single04 INSTANCE = null;

    /**
     * 构造器必须私有化,不允许外部进行对象的创建
     */
    private Single04() {
    }

    public static synchronized Single04 getINSTANCE() {
        // 加锁 保证线程安全,但是这种方式带来了 效率下降的问题
        if (INSTANCE == null) {
            // 此处验证多线程并发的问题,虽然此处达到了 按需分配,但是多线程时不安全
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Single04();
        }
        return INSTANCE;
    }

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

5 饿汉模式(解决性能问题)

此种存在问题,通过代码块未达到目的,时6 的过渡。

package com.xin.demo.sigle;

/**
 * 饿汉模式,按需分配
 */
public class Single05 {

    private static Single05 INSTANCE = null;

    /**
     * 构造器必须私有化,不允许外部进行对象的创建
     */
    private Single05() {
    }

    public static Single05 getINSTANCE() {

        if (INSTANCE == null) {
            // 此处验证多线程并发的问题,虽然此处达到了 按需分配,但是多线程时不安全
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 为了提高效率 通过用代码块的方式进行解决,如果为null 则加锁,此方法 依然不可行,未解决多线程的问题
            synchronized (Single05.class) {
                // 多线程时 第一处多个为null,有多个线程进了此锁,依然会产生多个对象
                INSTANCE = new Single05();
            }
        }
        return INSTANCE;
    }

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

6 饿汉模式(完美的饿汉模式)

package com.xin.demo.sigle;

/**
 * 饿汉模式,按需分配
 */
public class Single06 {

    private static Single06 INSTANCE = null;

    /**
     * 构造器必须私有化,不允许外部进行对象的创建
     */
    private Single06() {
    }

    public static Single06 getINSTANCE() {

        // 为了提高效率,如果为null 则不进锁
        if (INSTANCE == null) {
            // 此处验证多线程并发的问题,虽然此处达到了 按需分配,但是多线程时不安全
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 为了提高效率 通过用代码块的方式进行解决,如果为null 则加锁,此方法 依然不可行,未解决多线程的问题
            synchronized (Single06.class) {
                // 多线程时 第一处多个为null,有多个线程进了此锁,依然会产生多个对象
                // 因此 此处需要在进行一次判断,双重判断
                if (INSTANCE == null) {
                    INSTANCE = new Single06();
                }
            }
        }
        return INSTANCE;
    }

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

7 通过内部类实现

package com.xin.demo.sigle;

/**
 * 通过内部类 来实现加载外部类时不会加载内部类,这样可以实现懒加载
 */
public class Single07 {


    // 内部类不会在类加载的时候加载,只有在调用的时候进行加载,因此是按需分配,jvm保证了线程的安全
    public static class SingleHolder {
        protected static Single07 INSTANCE = new Single07();
    }

    /**
     * 构造器必须私有化,不允许外部进行对象的创建
     */
    private Single07() {
    }

    public static Single07 getINSTANCE() {
        return SingleHolder.INSTANCE;
    }

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

8 通过枚举实现

effective java 中建议采用 枚举来实习单例 ,推荐使用

package com.xin.demo.sigle;

/**
 * 通过 枚举保证了线程的安全,也可以防止反序列化
 */
public enum Single08 {
    INSTANCE;

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

9 通过枚举实现

枚举可以实现接口,无法进行继承,大多数情况下还是需要用到类的,因此我们通过在类中嵌套枚举以达到单列。

package com.xin.demo.sigle;

/**
 * 通过 枚举保证了线程的安全,也可以防止反序列化
 *
 * 枚举没法进行 继承,因此有时候通常需要类来进行操作,此处是将类 通过枚举来实现单列
 */
public class Single09 {


    public static Single09 getINSTANCE() {
        return Single09Enum.SINGLE09.getInstance();
    }

    public enum Single09Enum {
        SINGLE09;
        private final Single09 INSTANCE;

        Single09Enum() {
            INSTANCE = new Single09();
        }

        public Single09 getInstance() {
            return INSTANCE;
        }
    }

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

https://gitee.com/0x208_jackson/design-patterns

你可能感兴趣的:(设计模式——单例模式)