设计模式(一)-单例模式

1.单例模式【Singleton  Pattern】:

设计模式:对问题行之有效的解决方式。其实它是一种思想,当人们在写代码的过程中,发现有些代码可提高效率,或者可复用,或者更灵活,在不断的实践中,提取出的一些代码,根据特点命名,就出现了设计模式。

1,单例设计模式。
    解决的问题:就是可以保证一个类在内存中的对象唯一性。

UMl单例图:

饿汉式:这个类被加载的时候,静态变量会被初始化,类的对象就存在了。一加载,就创建这个唯一的对象。

    class EagerSingle {
    /**
     * 饿汉式
     * 这个类被加载的时候,静态变量会被初始化,类的对象就存在了
     */
    private static  final EagerSingle s = new EagerSingle();
    /**
     * 私有的默认的构造方法,避免外界通过构造方法new多个实例,
     * 构造方法是私有的,此类不能被继承
     */
    private EagerSingle(){
        
    }
    /**
     * 静态工厂方法
     * @return
     */
    public static synchronized  EagerSingle getInstance(){
        return s;
    }
    
    
}

 


懒汉式:类加载的时候,没有对象,只有调用getInstace()方法,对象才会被创建

class LazySingle{
        private static  LazySingle s1 = null;
        //私有的构造方法
        private LazySingle(){
            
        }
        /**
         * 加synchronized:
         * 原因:
         * 多个线程并发操作共享数据s1,因为有多条语句操作共享数据,会出现线程安全问题。
         * 可能创建多个对象,存在线程安全的问题,则用同步锁,
         * 在方法上加同步锁,或者加同步代码块,该类所属的类字节码锁
         * @return
         */
        public static synchronized LazySingle getInstance(){
            synchronized (LazySingle.class) {
                if(s1 != null){
                    //--A
                    //--B
                    //new 了不只一个对象
                    s1 = new LazySingle();
                }
            }
            
            return s1;
        }
    }
public class Single{
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            LazySingle ls1 = LazySingle.getInstance();
            LazySingle ls2 = LazySingle.getInstance();
            System.out.println(ls1==ls2);        }
    }

输出结果为:true。说明引用ls1和ls2 指向同一个对象,地址相同,堆内存中只存在一个对象。

单例设计模式的小应用:
class Test{
        private int num;
        private  static final Test t = new Test();
        private Test(){}
        public static synchronized Test getInstance(){
            return t;
        }
        public int getNum(){
            return num;
        }
        public void setNum(int num){
            this.num = num;
        }
    }
    public class Single{
        public static void main(String[] args) {            
       Test t1 = Test.getInstance(); Test t2 = Test.getInstance(); t1.setNum(10); t2.setNum(20); System.out.println(t1.getNum()); System.out.println(t2.getNum()); } }

输出结果是:20,20。而不是10,20,因为t1和t2指向同一个对象,t2.setNum(20)后面执行,对象的值被修改为20,所以都输出20.

 


 

2.单例模式的实现过程:

1.定义一个这个A类类型的静态变量 aprivate static A a = null;

2.private + 类名的构造函数,必须是私有的,是保证这个类不能被其它类new出来。

 Private A{}

3.对外提供一个一个public static A getInstance()的方法,其它类只能通过这个方法来得到这个类A的实例

4.通过new在本类中,将创建的对象返回

 

3. 加同步和不加同步的比较

方法二比方法一更加通用,更加完美,为什么呢?

因为方法一存在风险,方法一是线程不安全的,在一个B/S项目中,每个HTTP Request请求到J2EE的容器后就创建了一个线程,每个线程创建一个单例对象怎么办?假设这时候我们用方法一,大家看黄色部分,假如这会儿有两个线程A,B, 线程A执行到This.a = new A();在堆上正在申请内存空间,可能需要0.001微秒,就在这0.001微秒之内,线程B执行到 If (a == null),你说这个判断条件是true还是false呢?是true,因为线程A在堆上正在分配内存空间,还没有创建出实例来。那么,线程B也往下走,内存中就有两个a实例了,看看是不是出问题了?如果你这个单例是去拿一个序列号或者创建一个信号资源的时候,会怎么样?业务逻辑混乱!

数据一致性校验失败!最重要的是你从代码上还看不出什么问题,这才是最要命的!因为这种情况基本上你是重现不了的,不寒而栗吧,那怎么修改?用方法二,方法而通过finalsynchronized 两个关键字解决了以上假设的问题!synchronized 用来解决线程不安全的问题。

 

4. 单例模式的优缺点:

 

优点:

 

1.减少了内存的开支,特别是一个对象需要频繁的创建、销毁时,而且创建和销毁的性能又无法优化,单利模式的优势就非常明显。

 

2.减少了系统的性能开销,当一个对象的产生需要比较多的资源的时候,如读取配置、产生其它依赖对象时,则可以在应用启动时直接产生一个单例对象,然后永久驻留在内存的方式来解决(在java EE采用单例模式时需要注意JVM垃圾回收机制)

 

缺点:1..单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。

 

2.单例模式对测试是不利的。

 

5. 最佳实践:

 

单例模式应用非常广泛,如在Spring中,每个Bean默认就是单例singleton的,这样做的优点是Spring容器可以管理这些bean的生命周期,决定什么时候创建出来,什么时候销毁,销毁的时候如何处理,如果采用非单例模式(Prototype),则bean初始化的管理交由J2EE容器,Spring容器不在跟踪管理Bean的生命周期。(自己也在学Spring中用过

 

更全面的参考文章:https://coolshell.cn/articles/265.html

 

posted @ 2012-10-01 18:49  积淀  阅读(535)  评论(0编辑  收藏  举报