zoukankan      html  css  js  c++  java
  • 设计模式4

    单例 :单例模式确保一个类只有一个实例,并提供全局访问点,实现单例模式的方法是私有化构造函数,通过getInstance()方法实例化对象,并返回这个实例,并保证在JVM中只有一个实例

    单例模式优缺点

    优点

    1、单例类只有一个实例,不会频繁创建对象

    2、共享资源,全局使用,访问速度比较快(只有一个实例服用,不用频繁的创建)

    3、节省创建时间,提高性能

    缺点

    1、因为只有一个实例对象,多线程情况下,会出现线程安全问题

    2、单例实例化的对象,存放在方法区(java8 叫元空间)不会被回收,大量单例容易出现内存溢出问题(比如配置文件过大,实例化出来的类导致内存溢出)

    单例模式应用场景

    配置文件,Spring IOC容器默认单例,线程池和数据库连接池(复用机制),Servlet(线程不安全),JVM内置缓存Oscache,Ehcache (HashMap + 淘汰策略),枚举类,常量,计数器

    单例的七钟写法

    饿汉、懒汉(非线程安全)、懒汉(线程安全)、双重校验锁、静态内部类、枚举和容器类管理

    饿汉式

     * 饿汉式单例  就初始化一次
     * 优点;先天线程安全 => 当类被加载是就创建了实例
     * 缺点:static修饰的占用方法区,定义太多即使不使用也会占用内存,项目启动也会变慢
     **/
    public class Singleton饿汉式1 implements Serializable {
    
        private static Singleton饿汉式1 singleton饿汉式 = new Singleton饿汉式1();
    
        //无参构造函数 private
        private Singleton饿汉式1() {
        }
    
        public static Singleton饿汉式1 getSingleton饿汉式() {
            return singleton饿汉式;
        }
    
        public static void main(String[] args) {
            Singleton饿汉式1 singleton饿汉式1 = Singleton饿汉式1.getSingleton饿汉式();
            Singleton饿汉式1 singleton饿汉式2 = Singleton饿汉式1.getSingleton饿汉式();
            System.out.println(singleton饿汉式1 == singleton饿汉式2);
        }

    懒汉式(线程安全)

    如果不加锁就是线程不安全

     * 在被调用的时候才创建
     * 缺点: 线程不安全
     *  加锁synchronized 可以保证线程安全,但一般加synchronized锁意味着有线程等待
     **/
    public class Singleton懒汉式2 {
    
        private static Singleton懒汉式2 singleton懒汉式;
    
    
        //构造函数私有化
        private Singleton懒汉式2() {
            //防止java反射技术破解
            if(singleton懒汉式 != null){
                try {
                    throw new Exception("该对象已经被实例化。。。。");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
    
        public synchronized static Singleton懒汉式2 getSingleton懒汉式() {
    
            if(singleton懒汉式 == null){
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                singleton懒汉式 = new Singleton懒汉式2();
                return singleton懒汉式;
            }
            return singleton懒汉式;
        }
    
        public static void main(String[] args) {
           /* Singleton懒汉式 singleton懒汉式1 = Singleton懒汉式.getSingleton懒汉式();
            Singleton懒汉式 singleton懒汉式2 = Singleton懒汉式.getSingleton懒汉式();
            System.out.println(singleton懒汉式1 == singleton懒汉式2);*/
    
            for (int i = 0; i < 20; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Singleton懒汉式2 singleton懒汉式1 = Singleton懒汉式2.getSingleton懒汉式();
                        System.out.println(Thread.currentThread().getName() + singleton懒汉式1);
                    }
                }).start();
    
            }
        }
    }

    双重检验锁(DCL) 

     * 双重检验锁相比懒汉式,在实例化类的时候才加锁在读的时候不加锁,相比懒汉式减少了线程在读时候等待的问题
     * 但是在实例化的过程加了锁还是存在线程等待的问题
     *
     **/
    public class Singleton双重检验锁3 {
        private volatile static Singleton双重检验锁3 singleton双重检验锁3;
    
    
        //构造函数私有化
        private Singleton双重检验锁3() {
        }
    
    
        public  static Singleton双重检验锁3 getSingleton双重检验锁3() {
    
            if(singleton双重检验锁3 == null){
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (Singleton双重检验锁3.class){
                    if(singleton双重检验锁3 == null){
                        singleton双重检验锁3 = new Singleton双重检验锁3();
                    }
                }
            }
            return singleton双重检验锁3;
        }
    
        public static void main(String[] args) {
    
            for (int i = 0; i < 20; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Singleton双重检验锁3 双重检验锁 = Singleton双重检验锁3.getSingleton双重检验锁3();
    
                        System.out.println(Thread.currentThread().getName() + 双重检验锁);
                    }
                }).start();
    
            }
        }
    
    }

    静态内部内形式

     *  继承了懒汉式和饿汉式的优点
     *          内部类在被调用的时候才会初始化对象
     **/
    public class Singleton静态内部类5 {
    
        private Singleton静态内部类5() {
            System.out.println("构造函数初始化。。。。");
        }
    
        public static class  SingletonUtil{
            private  static Singleton静态内部类5 singleton静态内部类 = new Singleton静态内部类5();
        }
    
    
        public static void main(String[] args) {
            System.out.println("main方法启动。。。。");
            Singleton静态内部类5 singleton静态内部类1 = SingletonUtil.singleton静态内部类;
            Singleton静态内部类5 singleton静态内部类2 = SingletonUtil.singleton静态内部类;
            System.out.println(singleton静态内部类1 == singleton静态内部类2);
        }
    }

    枚举形式

    枚举形式能够先天性,防止反射和序列化破解单例。

    public enum Singleton枚举单例8 {
        SINGLETON_枚举单例;
    
        private String name;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            System.out.println("----getName----: " +name);
            return name;
        }
    
        public static void main(String[] args) throws Exception {
            Singleton枚举单例8 枚举单例1 = Singleton枚举单例8.SINGLETON_枚举单例;
            Singleton枚举单例8 枚举单例2 = Singleton枚举单例8.SINGLETON_枚举单例;
            枚举单例1.setName("kawa");
            枚举单例2.getName();
            System.out.println(枚举单例1 == 枚举单例2);
    
    
            //序列化破解发现获取的是同一个对象
            //序列化
            Singleton枚举单例8 枚举单例4 = Singleton枚举单例8.SINGLETON_枚举单例;
            FileOutputStream fos = new FileOutputStream(System.getProperty("user.dir")+"\Singleton2.txt");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(枚举单例4);
            oos.flush();
            oos.close();
    
            //反序列化
            FileInputStream fis = new FileInputStream(System.getProperty("user.dir")+"\Singleton2.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            Singleton枚举单例8 枚举单例5 = (Singleton枚举单例8) ois.readObject();
            System.out.println(枚举单例4==枚举单例5);
    
    
    
            //反射机制破解 会直接抛出异常 NoSuchMethodException
            Constructor<Singleton枚举单例8> constructor = Singleton枚举单例8.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            Singleton枚举单例8 枚举单例3 = constructor.newInstance();
            System.out.println(枚举单例3 == 枚举单例2);
    
    
        }

    使用容器管理 

    public class SingletonManager {
        private static Map<String, Object> objMap = new HashMap<String, Object>();
        public static void registerService(String key, Object instance) {
            if (!objMap.containsKey(key)) {
                objMap.put(key, instance);
            }
        }
        public static Object getService(String key) {
            {
                return objMap.get(key);
            }
        }
    }

    如何防止破坏单例这种使用SingletonManager 将多种单例类统一管理,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

    最后额外补充下,对象创建的方式:

    1. 通过 new 创建

    2. 通过反射 (如:Class.forName() )创建

    3. 通过序列化创建

    4. 通过clone()方式创建

  • 相关阅读:
    哪种可以让程序员赚到更多钱?
    layer 弹框 很好用 页面交互不好弄!!!父子弹框的交互!
    博客导航
    扯淡扯着扯着就远了----关键字;宁静致远
    高驰涛——裸奔到北京的程序猿
    TP5分页类使用——超级简单好用
    七牛云同步资源工具使用说明
    短链接实现原理和简单调用
    抓包工具Charles下载地址及Charles配置https
    敲代码的少年
  • 原文地址:https://www.cnblogs.com/hlkawa/p/13284956.html
Copyright © 2011-2022 走看看