zoukankan      html  css  js  c++  java
  • 单例模式--java代码实现

    单例模式

      单例模式,顾名思义,在程序运行中,实例化某个类时只实例化一次,即只有一个实例对象存在。例如在古代,一个国家只能有一个皇帝,在现代则是主席或总统等。

      在Java语言中单例模式有以下实现方式

    1.饿汉式

    import org.junit.jupiter.api.Test;
    
    public class Singleton {
        //静态成员变量
        private static Singleton singleton = new Singleton();
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
        //构造函数私有化
        private Singleton(){
        }
        //返回静态资源中Singleton实例
        public static Singleton getInstance() {
            return singleton;
        }
        /**
         * 单元测试
         */
        @Test
        public void testSingleton() {
            //通过调用类的静态方法返回类的实例对象
            Singleton s1 = Singleton.getInstance();
            Singleton s2 = Singleton.getInstance();
            s1.setName("zhangsan");
            s2.setName("lisi");
            System.out.println(s1.getName());
            System.out.println(s2.getName());
        }
    }

      在类加载时,直接将实例对象初始化,并且该实例是静态的,属于类的成员变量,通过调用类的静态方法返回该对象。  

      运行testSingleton单元测试,输出的两行都是lisi,因为s1,s2指向的同一个实例对象,这个对象在类创建的时候就存在了。

      饿汉式是线程安全的,不管系统的那一个线程获取这个对象,他们都是该类同一个对象。缺点是在程序在一开始就创建了该对象,占用内存空间。下面这种实现方式增加判断,在程序调用时才实例化该对象。

    2.懒汉式

    import org.junit.jupiter.api.Test;
    
    public class Singleton {
        //不初始化实例对象
        private static  Singleton singleton = null;
        private String name;
    
        public String getName() {
            return name;
        }
         //构造函数私有化
        private Singleton(){
        }
        public void setName(String name) {
            this.name = name;
        }
        //当被调用时才动态实例化
        public static  Singleton getInstance() {
            if(singleton == null) {    
                singleton = new Singleton();
            }
            return singleton;
        }
    }

      懒汉式解决了饿汉式的实例对象初始化占用内存的情况,但是懒汉式存在线程安全的问题,当多个线程同时访问getInstance()方法时,有可能在第一个线程进入if语句是还没new Singleton()时,这时第二个线程判断if的时候就会为真,则会创建新的实例,单例模式设计失败。

      这时需要在getInstance()方法上添加synchronized修饰符,表示线程同步,即当一个线程访问该方法时,其他线程需要等待其释放资源。

    public static synchronized Singleton getInstance() {
            if(singleton == null) {    
                singleton = new Singleton();
            }
            return singleton;
        }

      这样解决了懒汉式的线程不安全的问题,但是这样会降低系统的性能,当一个线程占用了该资源,其他的线程只能等待,系统的性能大大下降。于是乎第三种实现模式,对懒汉式进行了改进。

    3.双重检测

    public static  Singleton getInstance() {
            if(singleton == null) {    
                synchronized (Singleton.class) {
                    if(singleton==null) {
                        singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }

      双重检测,线程访问时有两次检测。

      这里将synchronized放到判断语句里面,这样当第一个线程调用getInstance()方法时,singleton为空,进入synchronized代码块,其他的线程即使也判断singleton为空,则需要等待第一个线程完成资源的使用,这样保证了线程的安全。

      当实例化对象完成时,其他的线程调用getInstance()方法时,直接返回Singleton对象即可,不用再等待,这一点优化了系统,提高了系统的性能。

    4.总结

      模式来源于生活。本文用Java语言实现了单例模式。三种实现方式都是线程安全的,饿汉式比较占用内存空间;懒汉式则降低了系统的性能;双重检测是懒汉式的升级,即能保证线程的安全,也能以懒加载的方式实现单例模式,在大规模系统中节省对象的创建时间,提高系统的性能。在系统中共享同一个资源或同一个对象,则可利用单例模式来实现。

      参考B站视频https://www.bilibili.com/video/av34162848

  • 相关阅读:
    安装Python 3.6 在Ubuntu 16.04 LTS 版本
    [leetcode]447. Number of Boomerangs
    【leetcode】443. String Compression
    第7课 课堂学习小问答
    第11章 进程间通信(4)_进程信号量
    第11章 进程间通信(3)_共享内存
    第11章 进程间通信(2)_消息队列
    第11章 进程间通信(1)_管道
    第10章 线程控制(5)_多线程下的fork
    第10章 线程控制(4)_多线程下的信号
  • 原文地址:https://www.cnblogs.com/peter_zhang/p/Singleton.html
Copyright © 2011-2022 走看看