zoukankan      html  css  js  c++  java
  • 二、单例模式之单例模式

    单例模式创建方式有以下几种方式:

    1. 饿汉模式
    2. 懒汉模式
    3. 注册式模式
    4. 枚举式模式
    5. 序列化模式

    1.饿汉模式

    在类加载时初始化,也是利用类加载线程安全的特性确保了单例实例化的线程安全。

    package com.kancy.pattern.single;
    
    /**
     * 单例模式 - 饿汉模式
     * @author kancy
     */
    public class HungerSingleton {
        /**
         * 在类加载时初始化好单例对象,利用类加载来保证单例初始化的线程安全
         * 优点:不用任何锁就能保证绝对线程安全,执行效率高
         * 缺点:由于类加载时就分配内存空间,初始化。如果以后使用不到改对象,就会造成一定程度内存空间的浪费。
         */
        private static HungerSingleton ourInstance = new HungerSingleton();
        private HungerSingleton() {
        }
        public static HungerSingleton getInstance() {
            return ourInstance;
        }
    }

    2.懒汉模式

    1)方式一:

    package com.kancy.pattern.single;
    
    /**
     * 单例模式 - 懒汉模式01
     * @author kancy
     */
    public class LazySingleton01 {
        /**
         * 单例被使用时才创建实例对象,不会造成内存空间浪费,但会存着数据安全问题:
         *      两个线程同一时刻判断instance == null 时,都会去创建对象,后者会覆盖前者引用。
         */
        private static LazySingleton01 instance;
        private LazySingleton01() {
        }
        public static LazySingleton01 getInstance(){
            if(instance == null){
                instance = new LazySingleton01();
            }
            return instance;
        }
    }

    2)方式二:

    package com.kancy.pattern.single;
    
    /**
     * 单例模式 - 懒汉模式02
     * @author kancy
     */
    public class LazySingleton02 {
        /**
         * 单例被使用时才创建实例对象,不会造成内存空间浪费,但会存着数据安全问题:
         *      两个线程同一时刻判断instance == null 时,都会去创建对象,后者会覆盖前者引用。
         *  加上方法可见锁,确保线程安全,但同时会较低执行效率,性能下降。
         */
        private static LazySingleton02 instance;
        private LazySingleton02() {
        }
        public synchronized static LazySingleton02 getInstance(){
            if(instance == null){
                instance = new LazySingleton02();
            }
            return instance;
        }
    }

    3)方式三:

    package com.kancy.pattern.single;
    
    /**
     * 单例模式 - 懒汉模式03
     * @author kancy
     */
    public class LazySingleton03 {
        /**
         * 单例被使用时才创建实例对象,不会造成内存空间浪费,但会存着数据安全问题:
         *      两个线程同一时刻判断instance == null 时,都会去创建对象,后者会覆盖前者引用。
         *  使用对象锁,缩小锁的范围,确保线程安全的同时,减小性能的损失
         */
        private static volatile LazySingleton03 instance; // 保证对象在内存中的可见性
        private LazySingleton03() {
        }
        public static LazySingleton03 getInstance(){
            if(instance == null){
                synchronized (LazySingleton03.class){
                    // double check,防止对象没有初始化完整时,第二个线程再次进来进行初始化
                    if(instance == null){
                        instance = new LazySingleton03();
                    }
                }
            }
            return instance;
        }
    }

    4)方式四:

    package com.kancy.pattern.single;
    
    import java.util.concurrent.atomic.AtomicBoolean;
    
    /**
     * 单例模式 - 懒汉模式04
     * @author kancy
     */
    public class LazySingleton04 {
        /**
         * 可以通过反射修改initFlg再次进行反射初始化对象,目前没有好的办法解决
         *  但一般没人这么无聊吧!!!
         */
        private static AtomicBoolean initFlg = new AtomicBoolean(false);
        private LazySingleton04() {
            // 防止反射入侵:不允许再次创建实例
            if(!initFlg.get()){
                initFlg.set(true);
            }else{
                throw new RuntimeException("单例被反射入侵");
            }
        }
        /**
         * 利用内部类加载的机制巧妙的实现单例的线程安全。
         * 内部类在使用时才会进行类加载。
         * 当调用getInstance方法时,才会把内部类LazySingleton04Holder加载到内存,同时线程安全的初始化LazySingleton04实例。
         * 这样既可以保证线程安全,也可以减少内存浪费的机会。
         */
        public static LazySingleton04 getInstance(){
            return LazySingleton04Holder.instance;
        }
        private static class LazySingleton04Holder{
            private static final LazySingleton04 instance = new LazySingleton04();
        }
    }

    效率从高到低:LazySingleton04 - > LazySingleton01 -> LazySingleton03 -> LazySingleton02,所以推荐使用第四种。

    3.注册式

    把单例注册到容器中,有则取出,无则创建并注册

    package com.kancy.pattern.single;
    
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * 单例模式 - 注册式单例模式
     */
    public class RegisterSingleton {
        /**
         * 读取实现线程安全
         */
        private static Map<String,Object> ioc = new ConcurrentHashMap();
        public static Object getBean(String className){
            if(!ioc.containsKey(className)){
                try {
                    // 创建对象,并且注册到容器当中
                    Class<?> aClass = Class.forName(className);
                    Object instance = aClass.newInstance();
                    // 创建对象到put进入ioc中需要一段时间,可能造成线程不安全
                    ioc.put(className, instance);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return ioc.get(className);
        }
    }

    4.枚举式

    package com.kancy.pattern.single;
    /**
     * 单例模式 - 常量/枚举式单例模式
     */
    public enum EnumSingleton {
        A,B,C;
    }

    5.序列化方式

    序列化:将内存状态转化为字节数组,持久化到磁盘等。

    反序列化:将持久化内容转化为java对象。

    附上测试类代码:

    package com.kancy.pattern.single;
    
    import com.kancy.bean.Person;
    
    import java.io.Serializable;
    import java.util.concurrent.CountDownLatch;
    
    public class SingletonTest implements Serializable,Cloneable{
    
        public static void main(String[] args) {
            testDataSecure();
        }
        /**
         * 测试数据安全
         */
        private static void testDataSecure() {
            int threadCount = 20;
            CountDownLatch count = new CountDownLatch(threadCount);
            for (int i = 0; i < threadCount; i++) {
                new Thread(() -> {
                    try {
                        // 等待所有线程都创建好,一起去执行
                        count.await();
                        Object bean = RegisterSingleton.getBean(Person.class.getName());
                        System.out.println(bean);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }).start();
                // 线程就绪
                count.countDown();
            }
        }
        /**
         * 懒汉效率从高到底:
         *  LazySingleton04 - > LazySingleton01 -> LazySingleton03 -> LazySingleton02
         */
        private static void testEfficiency() {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 100000000; i++) {
                LazySingleton04.getInstance();
            }
            long end = System.currentTimeMillis();
            System.out.println("用时:" + (end - start));
        }
    }

    kancy
  • 相关阅读:
    HDU 5835 Danganronpa 贪心
    HDU 5842 Lweb and String 水题
    HDU 5832 A water problem 水题
    Codeforces Beta Round #14 (Div. 2) A. Letter 水题
    Western Subregional of NEERC, Minsk, Wednesday, November 4, 2015 Problem K. UTF-8 Decoder 模拟题
    Western Subregional of NEERC, Minsk, Wednesday, November 4, 2015 Problem I. Alien Rectangles 数学
    Western Subregional of NEERC, Minsk, Wednesday, November 4, 2015 Problem H. Parallel Worlds 计算几何
    Western Subregional of NEERC, Minsk, Wednesday, November 4, 2015 Problem F. Turning Grille 暴力
    Western Subregional of NEERC, Minsk, Wednesday, November 4, 2015 Problem C. Cargo Transportation 暴力
    Western Subregional of NEERC, Minsk, Wednesday, November 4, 2015 Problem G. k-palindrome dp
  • 原文地址:https://www.cnblogs.com/kancy/p/10226960.html
Copyright © 2011-2022 走看看