设计模式——单例模式

一、引言

  今天笔者想写的设计模式——单例模式,其意图就是为了使系统有且仅有一个实例化,也就是一个对象我只有new一次就够了,也是像我们平凡人一样,婚结一次就够了,但是现在的社会啊,我只能以“理想很丰满,现实很骨感”来形容了,让我们一起了解下单例设计模式吧。

二、单例模式

  1. 定义:它是比较简单的一个模式,就是确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

  2. 类图:单纯的单例模式,其实就一个类,所以在此处就省略一下类图吧。

三、单例模式示例

  1. 懒汉式  

package com.pattern.singleton.core;

/**
 * 单例——懒汉式
 * 典型的以时间换空间方式
 * @author yemaoan
 *
 */
public class Singleton {

    private static Singleton singleton;
    
    private Singleton() {    //注意是private哦
        
    }
    
    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

  2. 饱汉式

package com.pattern.singleton.core;

/**
 * 单例——饿汉式
 * 典型的以空间换时间方式
 * @author yemaoan
 *
 */
public class Singleton {

    public static Singleton singleton = new Singleton();
    
    private Singleton() {
        
    }
    
    public static Singleton getInstance() {
        return singleton;
    }
}

四、单例模式应用

  单例看起来就是如上面的两种情况一样,相当简单,如果真的这么认为单例就这样完了,那你可能真的可能完了。说不定哪一天你的婚姻真的因插入一个小三给毁了,当然,言重了,那让我们分析一下弊病吧。

  在大数据的背景下,并发处理就不可或缺了,而只是简简单单的单例就可能经不起压力测试了,从源码分析上,很容易就发现在问题所在了。怎么解决了,暂且可以在getSingleton()方法前加个关键字synchronized来解决吧。

  解决线程不安全的方法挺多的,具体情况具体分析吧。笔者推荐一个:

package com.pattern.singleton.core;

/**
 * 利用static在JVM启动时只实例化一次确保对象唯一
 * @author yemaoan
 *
 */
public class Singleton {

    private Singleton() {
        
    }

    public static Singleton getInstance() {
        return SingletonInstance.instance;
    }

    //静态类——在Java应用启动只实例化一次,确实instance的唯一
    private static class SingletonInstance {
        static Singleton instance = new Singleton();
    }
    
}

   写个例子测试一下吧——就拿上文写的策略算法映射Service来写吧,为什么这么选择呢,也许有这么一个需求,笔者认为,这个类其实也就是一个服务类,很多细节是在开发前就已经配置好的,或者说在数据库里已经定义好的,这样的话,就想当一个工具类。

package com.pattern.stategy.factory;

import java.util.HashMap;
import java.util.Map;

import com.pattern.stratgy.core.Car;
import com.pattern.stratgy.core.Plane;
import com.pattern.stratgy.core.Train;

/**
 * 算法映射——改装成单例
 * @author yemaoan
 *
 */
public class ContentService {

    private Map<String, Class> contentMap = new HashMap<String, Class>();
    
    private ContentService() {
        contentMap.put("train", Train.class);
        contentMap.put("plane", Plane.class);
        contentMap.put("car", Car.class);
    }
    
    public static ContentService contentServiceInstance() {
        return ContentServiceInstance.instance;
    }

    private static class ContentServiceInstance {
        static ContentService instance = new ContentService();
    }
    
    public Class get(String key) {
        return contentMap.get(key);
    }
    
}

  调用者代码:

package com.pattern.singleton.client;

import java.util.ArrayList;
import java.util.List;

import com.pattern.stategy.factory.ContentService;

/**
 * 
 * @author yemaoan
 *
 */
public class ContentServiceThread extends Thread {
    
    private static List<ContentService> list = new ArrayList<ContentService>();
    private String name;
    
    public ContentServiceThread(String name) {
        this.name = name;
    }
    
    @Override
    public void run() {
        for(int index = 0; index < 5; index++) {
            ContentService contentService = ContentService.contentServiceInstance();
            list.add(contentService);
            System.out.print(this.name + "  ");
        }
    }
    
    public static void main(String[] args) throws InterruptedException  {
        //启动四线程,每线程生成5个ContentService
        //thread begin
        new ContentServiceThread("A").start();
        new ContentServiceThread("B").start();
        new ContentServiceThread("C").start();
        new ContentServiceThread("D").start();
        //thread end
        Thread.sleep(2000);
        System.out.println();
        System.out.println("当前对象总数为:" + list.size());
        for(int index = 0; index < list.size(); index++) {
            System.out.println((index+1) + " : " + list.get(index));
        }
    }
}

  笔者用了四个线程,每个线程生成5个ContentService对象,让我们看一下具体运行效果:

B  A  D  C  D  A  B  A  D  C  D  A  B  A  D  C  B  C  B  C  
当前对象总数为:20
1 : com.pattern.stategy.factory.ContentService@1034bb5
2 : com.pattern.stategy.factory.ContentService@1034bb5
3 : com.pattern.stategy.factory.ContentService@1034bb5
4 : com.pattern.stategy.factory.ContentService@1034bb5
5 : com.pattern.stategy.factory.ContentService@1034bb5
6 : com.pattern.stategy.factory.ContentService@1034bb5
7 : com.pattern.stategy.factory.ContentService@1034bb5
8 : com.pattern.stategy.factory.ContentService@1034bb5
9 : com.pattern.stategy.factory.ContentService@1034bb5
10 : com.pattern.stategy.factory.ContentService@1034bb5
11 : com.pattern.stategy.factory.ContentService@1034bb5
12 : com.pattern.stategy.factory.ContentService@1034bb5
13 : com.pattern.stategy.factory.ContentService@1034bb5
14 : com.pattern.stategy.factory.ContentService@1034bb5
15 : com.pattern.stategy.factory.ContentService@1034bb5
16 : com.pattern.stategy.factory.ContentService@1034bb5
17 : com.pattern.stategy.factory.ContentService@1034bb5
18 : com.pattern.stategy.factory.ContentService@1034bb5
19 : com.pattern.stategy.factory.ContentService@1034bb5
20 : com.pattern.stategy.factory.ContentService@1034bb5

五、总结

  1. 单例在设计模式中是比较简单的一个了,它是用途主要是保证一个类有且只有一个实例化对象。

  2. 单例在多线程中需要注意一下并发产生对象不一的情况,这也许在压力过程中就能体现出来。

六、题外话

  在这里就事论事地说一下,有人也许会认为直接把一个对象设置成一个全局变量就得了,何必这么纠结呢?呵呵,笔者认为,一个全局变量毕竟是一开始就要初始化创建,耗资源呐,再说倘若后期没用了,怎么办,岂不是白白浪费了......而采用单例的话,那又不同说法了,毕竟单例是我们需要的时候才去创建,再者单例模式可以确保一个对象只有被实例化一次。

posted @ 2013-10-06 20:41  swyma  阅读(917)  评论(0编辑  收藏  举报