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也是这个道理。

对于枚举的单利模式明天继续详细分析;睡觉睡觉。。。。。

原文地址:https://www.cnblogs.com/java-synchronized/p/6671303.html