首页 > 代码库 > 【Java设计模式】1.单例模式

【Java设计模式】1.单例模式

写在前面

Java设计模式总共有23种,虽然我也没仔细数。单例模式,好像在常用的Java项目中必不可少吧,好比是做米饭绝对少不了米,没毛病。这里谈谈自己的理解吧,大致分为几个方面:

1.哪些时候需要用到单例模式,即单例模式的使用场景,谈谈Singleton Mode的概念

2.常见有哪几种单例模式

3.单例模式的好处

4.单例模式和线程安全的那些事儿

1.0 什么时候需要单例模式

1.1 定义

单例模式,是一种创建对象的设计模式,单例模式确保其某一个类只有一个实例,即:它要确保整个类有且只有一个可供外界调用的实例化方法,并自动实例化,供整个Java项目(系统)的类调用。

这个类:单例类。

1.2 单例模式的使用场景

这里百度下:配置信息类、管理类、控制类、门面类、代理类通常被设计为单例类。像Java的Struts、spring框架,.Net的Spring.NET框架,以及PHP的Zend框架都大量使用了单例模式。

这里贴一个配置信息类代码,是我的项目中用到的全局配置文件

ResourceLoader.java,这个类就是一个单例,用于读取配置文件
技术分享
 1 package friends.util.common;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.util.HashMap;
 6 import java.util.Map;
 7 import java.util.Properties;
 8 
 9 /**
10  * Created by Samuel on 2017/5/2.
11  *
12  * Singleton mod(lazy-type)
13  *
14  */
15 public class ResourceLoader {
16 
17     private static ResourceLoader loader;
18     private static Map<String, Properties> loaderMap = new HashMap<String, Properties>();
19 
20     private ResourceLoader() {
21     }
22 
23     public static ResourceLoader getInstance() {
24         if(loader==null)
25             loader = new ResourceLoader();
26 
27         return loader;
28     }
29 
30     public Properties getPropFromProperties(String fileName) throws Exception {
31 
32         Properties prop = loaderMap.get(fileName);
33         if (prop != null) {
34             return prop;
35         }
36         String filePath = null;
37         String configPath = System.getProperty("configurePath");
38 
39         if (configPath == null) {
40 
41             // 加载classes的根目录
42             StringBuffer root_path = new StringBuffer(this.getClass().getClassLoader().getResource("/").getPath());
43 
44             //我们的配置文件是放在与classes的同级目录conf下的,故
45             filePath = root_path.append("../conf/").append(fileName).toString();
46 
47         } else {
48             filePath = configPath + "/" + fileName;
49         }
50         prop = new Properties();
51         prop.load(new FileInputStream(new File(filePath)));
52 
53         loaderMap.put(fileName, prop);
54         return prop;
55     }
56 }
View Code
PropertiesUtil.java
package friends.util.common;

import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Created by Samuel on 2017/5/2.
 */
public class PropertiesUtil {

    private static ResourceLoader loader = ResourceLoader.getInstance();
    //存储 配置文件中的k-v
    private static ConcurrentMap<String, String> configMap = new ConcurrentHashMap<String, String>();
    private static final String DEFAULT_CONFIG_FILE = "wxset.properties";

    private static Properties prop = null;

    public static String getStringByKey(String key, String propName) {
        try {
            prop = loader.getPropFromProperties(propName);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        key = key.trim();
        if (!configMap.containsKey(key)) {
            if (prop.getProperty(key) != null) {
                configMap.put(key, prop.getProperty(key));
            }
        }
        return configMap.get(key);
    }

    public static String getStringByKey(String key) {
        return getStringByKey(key, DEFAULT_CONFIG_FILE);
    }

    public static Properties getProperties() {
        try {
            return loader.getPropFromProperties(DEFAULT_CONFIG_FILE);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
Constant.java
 1 package friends.util.common;
 2 
 3 /**
 4  * Created by Samuel on 2017/5/2.
 5  *
 6  * 全局常量 的配置
 7  */
 8 public class Constant {
 9 
10     /**
11      * 微信的appid
12      */
13     public static final String appid_wx = PropertiesUtil.getStringByKey("appid");
14 
15 
16 }

综上,想要调用,配置文件中的一些参数,在Constant都定义好了,直接调用即可。

其他的场景我就没法演示了,可能以前遇到多,但是没注意(鄙视自己100次),也许就没碰到过,好吧,言归正题自己侃...

2.常见有哪几种单例模式

Java设计模式有23种,而单例模式的实现方式虽说没那么多,但是也不少,之前看过一篇博文,谈到了7种之多,点这里

这里点一下,Effective Java提到的enum枚举的方式来实现单例模式,这里我就讲讲常用的懒汉式(lazy-type)和饿汉式

2.1  懒汉式

     所谓懒汉式,就是很懒了,你不搭理我,我就不干活,说到底就是不自觉啦。代码如上 ResourceLoader.java 中的 loader 预先只是申明,并没有实例化,调用实例化方法getInstance()时才取判断是否实例化情况来实例化,这就是典型的懒汉,为啥是懒汉不是懒鬼,懒婆,一言蔽之:"国际惯例"!

2.2 饿汉式

    相比于懒汉式,它自然就是个饿鬼了,一回家,不用别人催它,看到香喷喷的饭菜就吃,我改一下ResourceLoader.java ,贴一下,大家对比下,就知道什么叫饿鬼了,额,饿汉

ResourceLoader2.java
技术分享
 1 package friends.util.common;
 2 
 3 /**
 4  * Created by Samuel on 2017/5/2.
 5  *
 6  * Singleton mod 饿汉式
 7  *
 8  */
 9 public class ResourceLoader2 {
10 
11     /**
12      * 申明 私有化 静态实例,仅供实例方法调用
13      */
14     private static ResourceLoader2 loader = new ResourceLoader2();;
15 
16     /**
17      * 和 懒汉式一样,私有化构造方法,不让其他类调用
18      */
19     private ResourceLoader2() {
20     }
21 
22     public static ResourceLoader2 getInstance() {
23         return loader;
24     }
25 
26 
27 }
View Code

ResourceLoader2.java 中的 loader 在项目启动的时候就直接实例化了(太饿了),调用实例化方法getInstance()时直接获得实例,

3.单例模式的好处

1、控制资源的使用,通过线程同步来控制资源的并发访问

2、控制实例的产生,以达到节约资源的目的

3、控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信

这三点摘自 师哥的总结,后两点我想应该很明显吧,至于第一点,看下面的分解

4.单例模式和线程安全的那些事儿

4.1 懒汉式,是线程不安全的,多线程的情况下是致命的,

4.2 有人说在实现方法getInstance()加上关键字synchronized 不就线程安全了吗?是的,随之带来的,效率太低了

4.3 饿汉模式,是线程安全的,这种方式基于classloder机制避免了多线程的同步问题,但是没有懒加载的效果

 

这里备注下;之前看过一篇博文,谈到了7种之多,这篇博文中谈到单例模式的2个坑,这里借鉴下,我觉得很重要

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

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

对第一个问题修复的办法是:

 1 private static Class getClass(String classname)    
 2                                          throws ClassNotFoundException {   
 3       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();   
 4     
 5       if(classLoader == null)   
 6          classLoader = Singleton.class.getClassLoader();   
 7     
 8       return (classLoader.loadClass(classname));   
 9    }   
10 }

对第二个问题修复的办法是:

 1 public class Singleton implements java.io.Serializable {   
 2    public static Singleton INSTANCE = new Singleton();   
 3     
 4    protected Singleton() {   
 5       
 6    }   
 7    private Object readResolve() {   
 8             return INSTANCE;   
 9       }  
10 } 

最后

又无耻的摘了果子,不耻下问,共同学习,向大神敬个礼!

 

【Java设计模式】1.单例模式