设计模式-单例模式

/ 技术相关 / 0 条评论 / 1260浏览

我的时光机设计模式-23种设计模式类图----下一篇预告----设计模式-工厂方法模式


相信大家已经看过我的上一篇文章,对23种设计模式有了大致的了解,下面我将和大家一起来学习每一种模式。

说到单例模式,我们先一起看下它的定义:Ensure a class has only one instance,and provide a global point of access to it.(确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例。) 动动手,动动脑,一起写代码,整个代码会放在我的github上,文章结尾会附上链接地址。废话不多说,我们一起看下单例模式的几种实现方式,有不准确的地方,请多指正。

懒汉式

/**
 * 懒汉式
 *
 * @author YYC
 * @date 2018/6/7
 */
public class Singleton1 {
    private static Singleton1 singleton = null;
    private Singleton1(){
        System.out.println("生成对象Singleton1》》》》》》》》》》》");
    }

    public static Singleton1 getSingleton() {
        if (singleton == null) {
            try {
                //线程暂停10毫秒,复现高并发下单例问题
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            singleton = new Singleton1();
        }
        return singleton;
    }

    public void say(){
        System.out.println("Singleton1.say : hello Singleton1 !");
    }
}

懒汉式加锁

/**
 * 懒汉式加锁
 *
 * @author YYC
 * @date 2018/6/7
 */
public class Singleton2 {
    private static Singleton2 singleton = null;

    private Singleton2() {
        System.out.println("生成对象Singleton2》》》》》》》》》》》");
    }

    public static Singleton2 getSingleton() {
        //同步块,线程安全的创建实例
        synchronized (Singleton2.class) {
            if (singleton == null) {
                try {
                    //线程暂停10毫秒,复现高并发下单例问题
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                singleton = new Singleton2();
            }
        }
        return singleton;
    }

    public void say() {
        System.out.println("Singleton2.say : hello Singleton2 !");
    }
}

双重检查加锁

/**
 * 双重检查加锁
 *
 * @author YYC
 * @date 2018/6/7
 */
public class Singleton3 {
    private static Singleton3 singleton = null;

    private Singleton3() {
        System.out.println("生成对象Singleton3》》》》》》》》》》》");
    }

    public static Singleton3 getSingleton() {
        //先检查实例是否存在,如果不存在才进入下面的同步块
        if(singleton == null) {
            try {
                //线程暂停10毫秒,复现高并发下单例问题
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //同步块,线程安全的创建实例
            synchronized (Singleton3.class) {
                //再次检查实例是否存在,如果不存在才真正的创建实例
                if (singleton == null) {
                    try {
                        //线程暂停10毫秒,复现高并发下单例问题
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    singleton = new Singleton3();
                }
            }
        }
        return singleton;
    }

    public void say() {
        System.out.println("Singleton3.say : hello Singleton3 !");
    }
}

饿汉式

/**
 * 饿汉式
 *
 * @author YYC
 * @date 2018/6/7
 */
public class Singleton4 {
    private static Singleton4 singleton = new Singleton4();

    private Singleton4() {
        System.out.println("生成对象Singleton4》》》》》》》》》》》");
    }

    public static Singleton4 getSingleton() {
        try {
            //线程暂停10毫秒,复现高并发下单例问题
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return singleton;
    }

    public void say() {
        System.out.println("Singleton4.say : hello Singleton4 !");
    }
}

枚举实现单例

/**
 * 枚举实现单例
 *
 * @author YYC
 * @date 2018/6/7
 */
public enum Singleton5 {
    /**
     * 定义一个枚举的元素,它就代表了Singleton的一个实例。
     */

    singleton;

    /**
     * 单例可以有自己的操作
     */
    public void say(){
        System.out.println("Singleton5.say : hello Singleton5 !");
    }
}

类级内部类实现单例

/**
 * 类级内部类实现单例
 *
 * @author YYC
 * @date 2018/6/7
 */
public class Singleton6 {
    private Singleton6(){
        System.out.println("生成对象Singleton6》》》》》》》》》》》");
    }
    /**
     *    类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
     *    没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
     */
    private static class SingletonHolder{
        /**
         * 静态初始化器,由JVM来保证线程安全
         */
        private static Singleton6 singleton = new Singleton6();
    }

    public static Singleton6 getSingleton(){
        return SingletonHolder.singleton;
    }

    public void say(){
        System.out.println("Singleton6.say : hello Singleton6 !");
    }
}

有上限的多例模式

/**
 * 有上限的多例模式
 *
 * @author YYC
 * @date 2018/6/7
 */
public class Singleton7 {
    private String name;
    private Singleton7() {}
    private Singleton7(String name ) {
        this.name = name;
        System.out.println("生成对象:" + name + "》》》》》》》》》》》");
    }
    private static List<Singleton7> list = new ArrayList<>();
    /**
     * 葫芦娃总数
     */
    private static int max = 7;
    /**
     * 当前葫芦娃
     */
    private static int num = 7;

    //添加葫芦七兄弟
    static {
        for (int i = 1; i <= max; i++) {
            list.add( new Singleton7(i + "娃"));
        }
    }

    public static Singleton7 getSingleton() {
        try {
            //线程暂停10毫秒,复现高并发下单例问题
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Random random = new Random();
        num = random.nextInt(max);
        return list.get(num);
    }

    public void say() {
        System.out.println("Singleton7.say : hello Singleton" + list.get(num).name + " !");
    }
}

测试用例

/**
 * Created by YYC on 2018/6/7.
 */
public class SingletonTest {

    /**
     * 排队有三种通用策略:
     * 直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
     * 无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
     * 有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。
     */
    private BlockingQueue blockingQueue = new SynchronousQueue<>();
    //首先构造一个线程池,SynchronousQueue,队列初始化容量为10。该线程池核心容量为100,最大容量为1000,线程存活时间为1毫秒。
    private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(100, 1000, 1 , TimeUnit.MILLISECONDS, blockingQueue);

    /**
     * 懒汉式不加锁
     * @throws Exception
     */
    @Test
    public void getSingleton_1() throws Exception {
        Task1 task = new Task1();
        for (int i = 0; i < 100; i++) {
            threadPoolExecutor.execute(task);
        }
        Thread.sleep(1000);
    }

    /**
     * 懒汉式加锁
     * @throws Exception
     */
    @Test
    public void getSingleton_2() throws Exception {
        Task2 task = new Task2();
        for (int i = 0; i < 100; i++) {
            threadPoolExecutor.execute(task);
        }
        Thread.sleep(1000);
    }

    /**
     * 双重检查加锁
     * @throws Exception
     */
    @Test
    public void getSingleton_3() throws Exception {
        Task3 task = new Task3();
        for (int i = 0; i < 100; i++) {
            threadPoolExecutor.execute(task);
        }
        Thread.sleep(1000);
    }

    /**
     * 饿汉式
     * @throws Exception
     */
    @Test
    public void getSingleton_4() throws Exception {
        Task4 task = new Task4();
        for (int i = 0; i < 100; i++) {
            threadPoolExecutor.execute(task);
        }
        Thread.sleep(1000);
    }

    /**
     * 枚举式
     * @throws Exception
     */
    @Test
    public void getSingleton_5() throws Exception {
        Task5 task = new Task5();
        for (int i = 0; i < 100; i++) {
            threadPoolExecutor.execute(task);
        }
        Thread.sleep(1000);
    }

    /**
     * 类级内部类
     * @throws Exception
     */
    @Test
    public void getSingleton_6() throws Exception {
        Task6 task = new Task6();
        for (int i = 0; i < 100; i++) {
            threadPoolExecutor.execute(task);
        }
        Thread.sleep(1000);
    }

    /**
     * 有上限的多例模式
     * @throws Exception
     */
    @Test
    public void getSingleton_7() throws Exception {
        Task7 task = new Task7();
        for (int i = 0; i < 100; i++) {
            threadPoolExecutor.execute(task);
        }
        Thread.sleep(1000);
    }

    class Task1 implements Runnable {
        @Override
        public void run() {
            Singleton1 singleton = Singleton1.getSingleton();
            singleton.say();
        }
    }

    class Task2 implements Runnable {
        @Override
        public void run() {
            Singleton2 singleton = Singleton2.getSingleton();
            singleton.say();
        }
    }

    class Task3 implements Runnable {
        @Override
        public void run() {
            Singleton3 singleton = Singleton3.getSingleton();
            singleton.say();
        }
    }

    class Task4 implements Runnable {
        @Override
        public void run() {
            Singleton4 singleton = Singleton4.getSingleton();
            singleton.say();
        }
    }

    class Task5 implements Runnable {
        @Override
        public void run() {
            Singleton5.singleton.say();
        }
    }

    class Task6 implements Runnable {
        @Override
        public void run() {
            Singleton6 singleton = Singleton6.getSingleton();
            singleton.say();
        }
    }

    class Task7 implements Runnable {
        @Override
        public void run() {
            Singleton7 singleton = Singleton7.getSingleton();
            singleton.say();
        }
    }
}