4.设计模式---单例模式(上)
单例模式有一下特点:
1、单例类只能有一个实例。
2、单例类必须自己自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
根据上面单利的特点,编写一个单利模式其实很简单:
饿汉:
1 package single.method; 2 /** 3 * 饿汉 4 * @author zengrong 5 * 6 */ 7 public class eSingle { 8 /** 9 * 在这个类被加载时,静态变量single会被初始化, 10 * 此时类的私有构造子会被调用。这时候,单例类的唯一实例就被创建出来了。 11 */ 12 private static eSingle single =new eSingle(); 13 /** 14 * 私有化构造函数 15 */ 16 private eSingle(){} 17 /** 18 * 对外界唯一的公开生成实例的方法 19 * @return 20 */ 21 public eSingle getInESingle() { 22 return single; 23 24 } 25 26 27 28 29 }
饿汉式和他的名字一样:类一加载就生成实例:
饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。
懒汉:(线程不安全模式)
package single.method;
/**
* 懒汉式(线程不安全)
* @author zengrong
*
*/
public class lSingle {
private static lSingle single=null;
private lSingle(){}
/**
*类加载时候并不生成实例,当调用时候才去加载生成实例
* @return
*/
public static lSingle getLSingle() {
return single==null ?new lSingle():single;
}
}
懒汉式:时间换取空间,在类加载时候并不加载生成实例,但是在多线程情况下,二个线程几乎同时到达getLSingle()方法,这样就会产生了多个实例,违背了我们当初的想法。
解决办法,双重加锁:
1 package single.method; 2 /** 3 * 改造懒汉模式变成线程安全 4 * @author zengrong 5 * 6 */ 7 public class lSingleSafety { 8 /** 9 * 用volatile修饰的变量, 10 * 线程在每次使用变量的时候, 11 * 都会读取变量修改后的最的值。 12 * volatile很容易被误用,用来进行原子性操作。 13 */ 14 private volatile static lSingleSafety singleSafety=null; 15 16 17 private lSingleSafety(){} 18 /** 19 * 采用的是双重加锁进行效率和安全 20 * @return 21 */ 22 public static lSingleSafety getLSingleSafety() { 23 24 if(singleSafety==null){ 25 synchronized (lSingleSafety.class) { 26 if(singleSafety == null){ 27 singleSafety= new lSingleSafety(); 28 } 29 } 30 } 31 return singleSafety; 32 } 33 34 }
所谓“双重检查加锁”机制,指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一重检查,进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。
“双重检查加锁”机制的实现会使用关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。
注意:在java1.4及以前版本中,很多JVM对于volatile关键字的实现的问题,会导致“双重检查加锁”的失败,因此“双重检查加锁”机制只只能用在java5及以上的版本。
在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括:
1.由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时
2.访问final字段时
3.在创建线程之前创建对象时
4.线程可以看见它将要处理的对象时
- 类级内部类?
简单点说,类级内部类指的是,有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。
类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
类级内部类中,可以定义静态的方法。在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
类级内部类相当于其外部类的成员,只有在第一次被使用的时候才被会装载。
根据上面的这些:
那么我们是不是可以在创建一个线程安全且又不用同步锁:
1 package single.method; 2 /** 3 * 使用类级别的静态类部类来进行实例化 4 * @author zengrong 5 * 6 */ 7 public class StaticSingle { 8 9 private StaticSingle(){} 10 11 private static class SingleOnInside{ 12 /** 13 * 初始化静态 14 */ 15 private static StaticSingle single=new StaticSingle(); 16 17 } 18 19 /** 20 * 安全问题由JVM来保证 21 * @return 22 */ 23 public static StaticSingle getStaticSingle() { 24 25 return SingleOnInside.single; 26 } 27 28 }
最牛逼的方法:高效,安全
枚举:
package single.method; /** * 枚举方法 * @author zengrong * */ public enum Singleton { /** * 定义一个枚举的元素,它就代表了Singleton的一个实例。 */ uniqueInstance; /** * 单例可以有自己的操作 */ public void singletonOperation(){ //功能处理 } }
单例模式的应用场景:
其中有些我也没用过,从其他博客看到的,记录下:
1. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~
2. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
3. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
4. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
5. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
6. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
7. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
8. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
9. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例.
放在JavaWeb里讲,对于一个web项目,servletContext这个上下文对象就是单例模式的体现,因为它对应着配置文件,是全局所共享的,jsp的内置对象application也是这个道理。
对于枚举的单利模式明天继续详细分析;睡觉睡觉。。。。。