java设计模式——单例模式

定义

         单例模式,是一种常用的软件设计模式,在他的核心结构中只包含一个被称为单例的特殊类。目的是保证系统中只有一个

实例。也就是一个类只有一个对象。

特点:

         1、单例类只有一个实例。

         2、单例类必须自己创建自己的唯一实例。

          3、单例类必须给所有其他对象提供这一实例。

单例模式的特点:

             1、私有的构造方法。

             2、指向自己实例的私有静态引用。

             3、以自己实例为返回值的静态的共有方法。

单例模式根据实例化对象时机的不同分为两种:

              一种是饿汉式单例,一种是懒汉式单例。

             饿汉式单例在单例类被加载时候,就实例化了一个对象交给自己的引用;

            懒汉式在调用取得实例方法的时候才会实例化对象。

 第一种:线程不安全(懒汉式)

public class Singleton{
    private static Singleton instance;
    private Singleton(){}
    public static Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

这种是懒加载,线程不安全,不推荐。

第二种:线程安全(懒汉式)

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

线程安全,但是效率极其低下,同步锁锁的是对象,每次取对象都有枷锁,因此不推荐。性能很低。

第三种,线程安全(饿汉式):

public class Singleton {
     private static Singleton instance = new Singleton3();
     private Singleton(){}
     public static synchronized Singleton getInstance(){
         return instance;
     }
 }

这种方式基于classloder机制避免了多线程的同步问题,但是instance在类加载的时候就实例化,这时候初始化instance

显然没有达到懒加载的效果。不推荐!

第四种,线程安全(饿汉模式):

public class Singleton{
    private static Singleton instance;
    static{
         instance=new Singleton;
    }
    private Singelton(){}
    public static synchronized Singleton gentInstance(){
        return instance;
    }

}

这种等同于第三种

第五种,线程安全(静态内部类)(常用)

public class Singleton{
    private Singleton(){}
    public static synchornized Singleton getInstance(){
        return SingletonHolder.instance;
    }
    private static class SingeletonHolder{
        private static Singleton instance=new Singleton();
    }
}

这种方式同样使用了classloder的机制来保证初始话instance时,只有一个线程,他跟第三种第四重方式不同的是:

第三种和第四种方式只要Singleton类别加载了,娜美instance就会被实例化(没有达到懒加载的效果),而这种方式是Singleton类被加载了,instance不一定别初始化,因为SingletonHolder类没有被主动使用,只有显示通过调动getInstance方法时,才会显示装载SingleHolder类,从而实例化instance。如果实例化instance很消耗资源,我想让他延迟加载,另一方面,我不希望在SingleHolder类加载时候就实例化,因为我不能确定SingleHolder类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的,这个时候,相比第三种第四章方式就显得很合理。

第六种(枚举):

public enum Singleton{
    INSTANCE;
    public void whateverMethod(){
    
    }

}

这种方式是Effective Java作者Josh Bloch 提倡的方式,他不仅能避免多线程同步问题,二期还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,这种方式不免让人 感觉陌生,在实际工作中,很少看到有人这么用。

单例模式还有一种比较常见的形式:双重锁的形式

public class Singleton{
    private static volatile Singleton intance=null;
    private Singleton(){}
    public static Singleton getInstance(){
        if(instace=null){
            sychronized(SingletonClass.Class){
                if(instance=null){
                    instance=new Singleton();
                }
            }
        }
    return instance;
    }
}

    这种方式:先检查实例是否存在,如果不存在才进入下面的同步代码块,在同步代码块中,线程安全的创建实例,

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

  双重检查加锁:
 * “双重检查加锁“的方式可以既实现线程安全,又能够使性能不受到很大的影响。
 * 那么什么是”双重检查加锁“机制呢?
 *        所谓双重检查加锁机制,指的是:并不是每次进入getInstance方法都需要同步,
 * 而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,
 * 这是第一重检查。进入同步块后,再次检查实例是否存在,如果不存在,就在同步的
 * 情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了
 * 多次在同步情况下进行判断所浪费的时间。
 *       双重检查加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile
 * 修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而
 * 确保多个线程能正确的处理该变量。
 *
 * 说明:由于volatile关键字可能会屏蔽掉虚拟机中的一些必要的代码优化,所以运行效率并不是
 * 很高。因此一般建议,没有特别的需要,不要使用。也就是说,虽然可以使用”双重检查加锁“
 * 机制来实现线程安全的单例,但并不建议大量采用,可以根据情况来选用。

总结:

          这里需要两问题注意:

                 1,如果单例有不同的类加载器加载,那便有可能存在多个单例类的实例。假定不是远端存取,假如一些servlet容器对每隔servlet使用都完全不同的类加载器,这样的话,如果有两个servlet访问同一个单例类,他们就都会有各自的实例。

          解决办法:

         

private static Class getClass(String classname)    
                                         throws ClassNotFoundException {   
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();   
    
      if(classLoader == null)   
         classLoader = Singleton.class.getClassLoader();   
    
      return (classLoader.loadClass(classname));   
   }   
}

                  2,如果Singleton实现了java.io.Serializable接口,那么这个累的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。

                 解决办法:

                        

public class Singleton implement java.io.Serializable{
    public static Singleton  InSTANCE= new Singleton();
    protect Singleton(){}
    private Object readResovle(){
        return INSTANCE;
    }
}

 

单例模式的优点:

                              1,在内存中只有一个对象,节省内存空间。

                               2,避免频繁的创建销毁对象,可以提高性能。

                               3,避免对共享资源的多重占用。

                               4,可以全局访问。

单例模式的缺点:

                            1,扩展困难,由于getInstance静态函数没办法生成子类的实例。如果要拓展,只有重写那个类。

                            2,隐式使用引起类结构不清晰。

                            3,导致程序内存泄露的问题。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值