• 设计模式之单件模式


        有人说单件模式是最简单的模式,由于它仅仅有一个类,但事实上它另一些值得注意的地方,就如:出现并发性时,单件可能已经不是单件了。

        先说一下,我们为什么要用到单件模式。当我们用到这些对象如:线程池,缓存。注冊表和日志对象等,其实,这些对象我们仅仅能有一个实例。不然会导致非常多问题出现,所以我们要将它弄成单件的。

        可能你会说利用程序猿之间的约定或是利用全局变量就能够做到啊。如:java的静态变量。是的,但全局变量有一个缺点。就是它必须在程序一開始就要创建好,万一这个对象非常耗费资源。而程序又一直没用到它。那不是非常浪费吗?而单件模式就克服了这个缺点。

        究竟怎样实现这个单件模式呢?首先这个类仅仅能实例化一次,那就说明它的构造方法不能公开;那不公开,我又怎样实例化它呢?事实上我们能够这样子做:(这样的是经典的单件模式实现)

    public class Singleton {
    	private static Singleton uniqueInstance;//利用一个静态变量来记录Singleton类的唯一实例
     
    	// other useful instance variables here
     
    	private Singleton() {}//仅仅有自己才干调用构造器
     
    	public static Singleton getInstance() {
                    //断点1(为以下文字解析使用)
    		if (uniqueInstance == null) {
                     //断点2
    			uniqueInstance = new Singleton();//当uniqueInstance不存在时才创建
    		}
    		return uniqueInstance;
    	}
     
    	// other useful methods here
    }

        假设你以为这样已经完美了。那就太天真了。

    在一个单线程中执行。这样确实已经完美了,但假设有非常多个线程并发的情况下,那单件可能就不是单件了。

        如:如今有两个线程A和B,当单件类还没实例化时,A和B线程都运行到断点1。由于单件类还没实例化,所以都能够运行断点2,当A这时先实例化单件类,尽管这时已经实例化单件类了,但这时已经不能阻止线程B再次运行实例化了(由于这时B已经进来了),这时就存在2个实例了。所以这样的经典的单件模式实现有点瑕疵。

        对于怎样处理多线程的问题,我这有三种解决方法。

        第一种:

    public class Singleton {
    	private static Singleton uniqueInstance;
     
    	// other useful instance variables here
     
    	private Singleton() {}
     
    	public static synchronized Singleton getInstance() {//比上面的代码中多了一个synchronized 
    		if (uniqueInstance == null) {
    			uniqueInstance = new Singleton();
    		}
    		return uniqueInstance;
    	}
     
    	// other useful methods here
    }

    没错,就是加线程锁来解决。要求进入getInstance()方法前,要等候别的线程离开该方法,才干运行。就是所不存在两个或以上的线程同一时候进入这种方法。

    但事实上我们仅仅有第一次运行此方法时才真正须要同步,创建好uniqueInstance变量后,我们就不须要同步这种方法了。而同步会减少性能,是一种累赘。

        另外一种:

    public class Singleton {
    	
            //在静态初始化器中创建单件,保证了线程安全
            private static Singleton uniqueInstance = new Singleton();
    
    	private Singleton() {}
     
    	public static Singleton getInstance() {
    		return uniqueInstance;
    	}
    }

    这样的就是一開始就创建好单件类,而不用延迟实例化的做法。假设创建和执行这个单件类时,负担不太繁重。就能够利用这样的方法创建单件,毕竟简单好用嘛。

        第三种:(我觉得最好的一种,只是须要java 5及以上版本号才干使用)

    public class Singleton {
    	private volatile static Singleton uniqueInstance;
     
    	private Singleton() {}
     
    	public static Singleton getInstance() {
                    //断点1
    		if (uniqueInstance == null) {
                      //断点2
    			synchronized (Singleton.class) {
                             //断点3
    				if (uniqueInstance == null) {
                                  //断点4
    					uniqueInstance = new Singleton();
    				}
    			}
    		}
    		return uniqueInstance;
    	}
    }

    首先解析volatile这个是有什么用的:用在多线程,同步变量。 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的訪问事实上訪问的是B。

    仅仅在某些动作时才进行A和B的同步。因此存在A和B不一致的情况。

    volatile就是用来避免这样的情况的。volatile告诉jvm, 它所修饰的变量不保留拷贝,直接訪问主内存中的(也就是上面说的A) ,但不保证原子操作。

        还是以两个线程A和B来说明,当单件类还没实例化时,线程A和B同一时候运行到断点1,推断完之后就会运行到断点2,这时如果A先快一步,运行到断点3并锁住类,线程B仅仅能等待,线程A推断完后。运行断点4并实例化单件类后,离开方法解锁。线程B运行到断点3并锁住类,这时推断实例uniqueInstance已存在,离开并解锁。后面其它一些线程仅仅会运行到断点1,并不会运行到断点2,这样就避免了线程锁所带来的性能问题了。

    注意锁前锁后的推断都是必须的。锁前是避免创建实例后其它线程运行线程锁方法,提高了性能;锁后,是避免创建多个实例,不然有多少个线程进入断点2,就会产生多少个实例。

    这就是“双重检查加锁”的方法。

        这就是单件模式。假设对其它的一些设计模式也感兴趣,欢迎查看我其它的一些博客。

    微笑


        转载请标明原文出处:http://blog.csdn.net/u012367513/article/details/31037887

  • 相关阅读:
    rabbitmq发送消息的两种格式:发送json数据和直接发送对象以及对json与对象之间的相互转换
    rabbitmq 的hello world程序
    rabbitmq用户管理、角色、权限管理以及UI管理界面的使用
    redis设置密码以及安装到服务
    mybatis- generator自动生成代码
    COGS 有标号的二分图计数系列
    Codeforces183D T-shirt
    bzoj3473 字符串
    51Nod1782 圣诞树
    51Nod1601 完全图的最小生成树计数
  • 原文地址:https://www.cnblogs.com/wzzkaifa/p/7049348.html
Copyright © 2020-2023  润新知