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

    介绍

    在编程开发中经常会遇到这样一种场景,那就是需要保证一个类只有一个实例哪怕多线程同时访问,并需要提供一个全局访问此实例的点。单例模式主要解决的是,一个全局使用的类频繁的创建和消费,从而提升提升整体的代码的性能。

    例如:日志系统的对象被反复创建打印

    public class Logger {
        private FileWriter writer;
    
        public Logger() {
            File file = new File("../log.txt");
            writer = new FileWriter(file, true); //true表示追加写入
        }
    
        public void log(String message) {
            writer.write(mesasge);
        }
    }
    // Logger类的应用示例:
    public class UserController {
        private Logger logger = new Logger();
    
        public void login(String username, String password) {
            logger.log(username + " logined!");
        }
    }
    
    public class OrderController {
        private Logger logger = new Logger();
    
        public void create(OrderVo order) {
            logger.log("Created an order: " + order.toString());
        }
    }
    

    每次打印日志都会生成log对象,造成了资源浪费,日常开发中大致上会出现如上这些场景中使用到单例模式,虽然单例模式并不复杂但是使用面却比较广。

    单例模式的实现

    要实现一个单例,需要关注的点无外乎下面几个:

    • 构造函数需要是 private 访问权限的,这样才能避免外部通过 new 创建实例
    • 考虑对象创建时的线程安全问题
    • 考虑是否支持延迟加载
    • 考虑 getInstance() 性能是否高(是否加锁)

    静态类使用

    public class Singleton_00 {
        public static Map<String,String> cache = new ConcurrentHashMap<String, String>(); 
    }
    

    在不需要维持任何状态下,仅仅用于全局访问,这个使用使用静态类的方式更加方便

    懒汉式(线程不安全)

    第一次被引用才会将自己实例化

    public class Singleton {
        private static Singleton singleton =null;//prvite堵死外界利用new创造实例的可能
        public Singleton(){
        }
        public static Singleton getInternece(){//全局中唯一能访问到实例的方法
            if(singleton ==null){//实例不存在就new一个
                 singleton = new Singleton();
            }
            return singleton;
        }
    }
    

    如果有多个访问者访问,就会造成多个同样的实例并存,从而没有达到单例的要求

    懒汉式加锁(线程安全)

    public class Singleton {
        private static Singleton singleton =null;//prvite堵死外界利用new创造实例的可能
        public Singleton(){
        }
        public static synchronized  Singleton getInternece(){//全局中唯一能访问到实例的方法
            if(singleton ==null){//实例不存在就new一个
                 singleton = new Singleton();
            }
            return singleton;
        }
    }
    

    如果频繁地用到,那频繁加锁、释放锁及并发度低等问题,会导致性能瓶颈,这种实现方式就不可取了

    饿汉式单例(线程安全)

    不管是否调用 先申请

    public  class Singleton {
        private static  Singleton singleton =new Singleton();
        public Singleton(){
        }
        public static Singleton getInternece(){
            return singleton;
        }
    }
    

    在类加载的时候,singleton静态实例就已经创建并初始化好了,所以singleton实例的创建过程是线程安全的

    静态内部类(线程安全)

    它有点类似饿汉式,但又能做到了延迟加载

    public class Singleton_04 {
    
        public  static  class SingletonHolder{
           private static Singleton_04 singleton04=new Singleton_04();
        }
    
        public  Singleton_04() {
        }
    
        public  static  Singleton_04 getInstance(){
            return SingletonHolder.singleton04;
        }
    }
    

    既保证了线程安全有保证了懒加载,同时不会因为加锁的方式耗费性能。非常推荐使用的一种单例模式

    双重锁定机制(线程安全)

    判断实例是否为空再上锁,volatile 关键字保障能保证线程有序性,禁止JVM以及处理器进行排序,线程安全效率又高

    public class Singleton {
        private static Singleton singleton =null;//prvite堵死外界利用new创造实例的可能
        private static volatile Object object=new Object();
        public Singleton(){
        }
        public static Singleton getInternece(){
                if(singleton ==null){//判断是否为空再上锁
                    synchronized (object)
                    {
                        singleton = new Singleton();
                    }
                }
            return singleton;
        }
    }
    

    但是这样如果有两个线程同时调用getInternece方法,都能通过singleton ==null,并且因为synchronized ,一个线程会先创建实例,第二个线程继续等待完毕之后又进入方法创建实例,就达不到单例的目的。

    public class Singleton {
        private static Singleton singleton = null;
        private static volatile Object object = new Object();
    
        public Singleton() {
        }
        public static Singleton getInternece() {
            if (singleton == null) {
                synchronized (object) {
                    if (singleton == null)
                        singleton = new Singleton();
                }
            }
            return singleton;
        }
    }
    

    枚举(线程安全 推荐)

    上面那些是不考虑反射机制和序列化机制的情况下实现的单例模式,但是如果考虑了反射,则上面的单例就无法做到单例类只能有一个实例这种说法了

      @Test
        public  void  Singleton() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
            Singleton_04 s1=Singleton_04.getInstance();
            Singleton_04 s2=Singleton_04.getInstance();
            System.out.println("s1:"+s1+"
    "+"s2:"+s2);
            System.out.println("正常模式单例"+(s1==s2));
    
            Constructor<Singleton_04> constructor=Singleton_04.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            Singleton_04 s3=constructor.newInstance();
            System.out.println("s3:"+s3+"
    "+"s1:"+s1);
            System.out.println("反射破坏单例"+(s2==s3));
        }
    

    image-20211009125300751

    实现枚举单例

    public enum  Singleton_06 {
        INSTANCE;
        public Singleton_06 getInstance(){
            return INSTANCE;
        }
    }
    

    反射破坏枚举

        @Test
        void  testEnum() throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
            Singleton_06 singleton06=Singleton_06.INSTANCE;
            Constructor<Singleton_06> constructor2=Singleton_06.class.getDeclaredConstructor();
            constructor2.setAccessible(true);
            Singleton_06 s4=constructor2.newInstance();
    
            System.out.println("s4:"+s4+"
    "+"singleton06:"+singleton06);
            System.out.println("反射破坏枚举单例"+(s4==singleton06));
        }
    

    破坏失败

    image-20211009125923849

  • 相关阅读:
    正则表达式
    HashTable与HashMap的区别
    求解连续子数组乘积的最大值
    求解N个值中最大的k个数,N远大于k
    C++权限修饰符
    DBSCAN算法
    【leetcode】1318. Minimum Flips to Make a OR b Equal to c
     【leetcode】1317. Convert Integer to the Sum of Two No-Zero Integers
    【leetcode】1316. Distinct Echo Substrings
    【leetcode】1315. Sum of Nodes with Even-Valued Grandparent
  • 原文地址:https://www.cnblogs.com/cg-ww/p/15386167.html
Copyright © 2011-2022 走看看