• android开发之路06(浅谈单例设计模式)


    设计模式之单例模式:

    .单例模式实现特点:①单例类在整个应用程序中只能有一个实例(通过私有无参构造器实现);②单例类必须自己创建这个实例并且可供其他对象访问(通过静态公开的访问权限修饰的getInstance()方法实现);

    简单实例1

    public class Singleton{

    private static Singleton uniqueInstance=null;

    /**

     * Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,

     * Singleton的唯一实例只能通过getInstance()方法访问。

     */

    private Singleton() {

    super();

    }

    /**

     * getInstance()方法特点:

     *  ①public修饰

     *      ②static修饰

     *  ③在其内部创建本类对象并赋给成员变量

     * 

     */

    public static Singleton getInstance() {

    if(uniqueInstance==null){

    uniqueInstance=new Singleton();

    }

    return uniqueInstance;

    }

    }

    简单实例2

    ①我们先在java工程中创建一个单例类

    public class Tools {

    private String name;

    private static Tools toolsInstance=null;

    private Tools(){

    }

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    public static Tools getInstance(){

    if(toolsInstance==null){

    toolsInstance=new Tools();

    }

    return toolsInstance;

    }

    }

    ②我们创建一个简单的测试类,验证单例设计特点是否正确

    public class TestTools {

    public static void main(String[] args) {

    /**

     * 在这里我们发现单例类的构造器是不可见的,无法通过new TestTools()来创建对象

     * 保证了类的内部信息对外不开放的原则,我们只能通过getInstance()方法得到唯一的

     * 单例对象,从而操作单例类的成员变量

     */

    Tools tool1=Tools.getInstance();

    tool1.setName("猪八戒");

    System.out.println(tool1.getName());

    Tools tool2=Tools.getInstance();

    tool2.setName("孙悟空");

    System.out.println(tool2.getName());

    System.out.println(tool1.getName());

    if(tool1==tool1){

    System.out.println("tool1和tool1是同一个对象");

    }

    }

    }

    .单例模式的应用扩展

    1.懒汉式模式:懒汉式是典型的用时间换空间,也就是每次获取实例都会进行判断,,看是否需要创建实例,因此浪费了一定的程序运行时间,但因此避免了不必要的内存空间的占用。

    2.饿汉式模式:饿汉式是典型的用空间换时间,当类加载的时候会创建类实例,故节省了运行时间,但可能浪费了不必要的内存空间。

    3.登记式模式:登记式实际对一组单例模式进行的维护,主要是在数量上的扩展,通过map我们把单例存进去,这样在调用时,先判断该单例是否已经创建,是的话直接返回,不是的话创建一个登记到map中,再返回。对于数量又分为固定数量和不固定数量的。下面采用的是不固定数量的方式,在getInstance方法中加上参数(string name)。然后通过子类继承,重写这个方法将name传进去。

    实例代码如下:

    //类似Spring里面的方法,将类名注册,下次从里面直接获取。  

    public class Singleton{  

    private static Map<String,Singleton> map = new HashMap<String,Singleton>();  

    //静态代码块

        static{  

            Singleton single = new Singleton();  

            map.put(single.getClass().getName(), single);  

        }  

        //保护的默认构造子  

        protected Singleton(){}  

        //静态工厂方法,返还此类惟一的实例  

        public static Singleton getInstance(String name) {  

            if(name == null) {  

                name = Singleton.class.getName();  

                System.out.println("name == null"+"--->name="+name);  

            }  

            if(map.get(name) == null) {  

                try {  

                    map.put(name, (Singleton) Class.forName(name).newInstance());  

                } catch (InstantiationException e) {  

                    e.printStackTrace();  

                } catch (IllegalAccessException e) {  

                    e.printStackTrace();  

                } catch (ClassNotFoundException e) {  

                    e.printStackTrace();  

                }  

            }  

            return map.get(name);  

        }  

         

        public static void main(String[] args) {  

            Singleton singleTest = Singleton.getInstance(null);  

            System.out.println(singleTest.about());  

        }  

    }  

    补充(静态代码块和静态方法的区别):如果有些代码必须在项目启动的时候就执行,就需要使用静态代码块,这种代码是主动执行的;需要在项目启动的时候就初始化但是不执行,在不创建对象的情况下,可以供其他程序调用,而在调用的时候才执行,这需要使用静态方法,这种代码是被动执行的静态方法在类加载的时候 就已经加载 可以用类名直接调用。

    三.线程安全:

    从线程安全的角度上讲,不加同步的懒汉式是线程不安全的,比如:有两个线程,一个是线程First,一个是线程Second,他们同时调用getInstance()方法,那就可能导致并发问题,因为在First线程中判断instance==null的时候会通过new 创建单例对象,但是此时,可能在Second线程中,也进行了同样的instance==null判断,因为此时,线程First中的对象还没有创建完成,所以线程Second中也会通过new来创建单例对象,那么问题就来了!同时会创建出两个实例来,也就是说单例的作用在并发的情况下失效了。

    而饿汉式是线程安全的,因为虚拟机只会装载一次,在装载类的时候是不会发生并发的。可以直接用于多线程而不会出现问题!那么,我们如何实现懒汉式的线程安全呢?很简单,我们只需要通过synchronized修饰即可解决问题,比如:

    Public static synchronized Singleton getInstance(){}但是这样一来,会降低整个访问的速度,而且每次都要判断。那么有没有更好的方式来实现呢?

    双重检查加锁:

    可以使用"双重检查加锁"的方式来实现,就可以既实现线程安全,又能够使性能不受到很大的影响。那么什么是"双重检查加锁"机制呢?

    所谓双重检查加锁机制,指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。

    双重检查加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

    示例代码如下:

    public class Singleton {  

          

        private volatile static Singleton instance = null;  

        private Singleton(){  

        }  

        public static Singleton getInstance(){  

            //先检查实例是否存在,如果不存在才进入下面的同步块  

            if(instance == null){  

                //同步块,线程安全地创建实例  

                synchronized(Singleton.class){  

                    //再次检查实例是否存在,如果不存在才真正地创建实例  

                    if(instance == null){  

                        instance = new Singleton();  

                    }  

                }  

            }  

            return instance;  

        }  

    这种实现方式可以实现既线程安全地创建实例,而又不会对性能造成太大的影响。它只是在第一次创建实例的时候同步,以后就不需要同步了,从而加快了运行速度。

  • 相关阅读:
    iOS 学习
    swift
    (九)Javabean与Jsp(来自那些年的笔记)
    (八)JSP 技术知识点总结(来自那些年的笔记)
    java抽象工厂模式
    java工厂方法模式
    java的简单工厂模式
    服务提供者框架讲解 之 myJDBC
    第一条:考虑用静态工厂方法代替构造器
    另辟蹊径的归并排序复杂度分析
  • 原文地址:https://www.cnblogs.com/smallkure/p/5248614.html
Copyright © 2020-2023  润新知