介绍
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。即一个类只有一个对象实例。
实际运用
例如一个嵌入到微信的项目,里面有这微信登入、微信分享文章、微信获取位置等功能。那么用到微信登入这个功能,首先就要获取access_token。下面是关于获取access_token的说明。
access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
公众平台的API调用所需的access_token的使用及生成方式说明:
1、建议公众号开发者使用中控服务器统一获取和刷新Access_token,其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突,导致access_token覆盖而影响业务;
2、目前Access_token的有效期通过返回的expire_in来传达,目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。在刷新过程中,中控服务器对外输出的依然是老access_token,此时公众平台后台会保证在刷新短时间内,新老access_token都可用,这保证了第三方业务的平滑过渡;
3、Access_token的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新access_token的接口,这样便于业务服务器在API调用获知access_token已超时的情况下,可以触发access_token的刷新流程。
当然,我这里只是介绍如何用单例模式保存这些信息,微信js的access_token也是如此。文中的map就是用来存储access_token和access_token创建时间。
分类
单例模式的创建方式可以分为两种,一种是饿汉式、一种是懒汉式。顾名思义饿汉式就是迫切的把实例初始化,懒汉式就是当调用时在进行初始化。
饿汉式
由于饿汉式篇幅较少,所以先介绍
public class EagerlySingleton {
private EagerlySingleton(){};
private static Map<String,Object> map = new HashMap<String, Object>();
/**
* <p>Description: 解决多线程问题方式二(饿汉)</p>
*/
public static synchronized Map<String,Object> getMemoryMap(){
return map;
}
}
代码见github地址
这种饿汉式的单例模式,首先解决了多线程的问题。EagerlySingleton 类创建的同时就已经创建好一个静态的对象供系统使用,所以是线程安全的。唯一的缺点就是EagerlySingleton 类太早被创建,而里面的map如果是很久才被使用,内存一直被占用着。
懒汉式
public class FirstSingleton {
private FirstSingleton(){};
private static Map<String,Object> map = null;
public static Map<String,Object> getMemoryMap()
{
if (map == null)
map = new HashMap<String, Object>();
return map;
}
}
代码见github地址
private FirstSingleton(){};私有构造器保证该类不被外部类实例化(当然反射可以突破这种限制)。通过调用getMemoryMap方法获得这个类中的map属性。但是这个方法不是线程安全的因为有可能第一个调用这个方法的已经正在创建map,然后第二个调用的进入if判断时map还是为空,然后就被覆盖了(怎么感觉map被覆盖了还是不会出问题的。。。)。
懒汉式-解决多线程问题方式一
public class SecondSingleton {
private SecondSingleton(){};
private static Map<String,Object> map = null;
/**
* <p>Description: 解决多线程问题方式一</p>
*/
public static synchronized Map<String,Object> getMemoryMap(){
if (map == null)
map = new HashMap<String, Object>();
return map;
}
}
代码见github地址
在getMemoryMap方法中加入synchronized 关键字将整个方法加锁,这样做是线程安全了,但是以后的每次调用都会加锁,其实只要在map实例化的时候加锁就行了,然后就出现双重检查(Double-check),如下
懒汉式-解决多线程问题方式三(方式二见饿汉式)
public class DoubleCheckSingleton {
private DoubleCheckSingleton(){};
private volatile static Map<String,Object> map = null;
/**
* <p>Description: 解决多线程问题方式三(双重检查加锁)</p>
*/
public static Map<String,Object> getMemoryMap2(){
if (map == null){
synchronized(DoubleCheckSingleton.class){
if (map == null){
map = new HashMap<String, Object>();
}
}
}
return map;
}
}
代码见github地址
双重检查加锁这种写法解决了线程问题及效率问题,然而还有一种静态内部类的写法。
懒汉式-解决多线程问题方式四
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton(){};
private static class MapHandle{
private volatile static Map<String,Object> map = new HashMap<String, Object>();
}
/**
* <p>Description: 解决多线程问题方式四(静态内部类)</p>
*/
public static final Map<String,Object> getMemoryMap(){
return MapHandle.map;
}
}
代码见github地址
静态内部类完美的解决了性能和线程安全问题。
总结
单例模式的介绍到这里就结束了,上面的map只是单例模式模式的一种运用(或许存在问题。。。),实际开发中还会有各种各样会用到单例模式的例子。
JDK中就有许多,例如Runtime 等等
public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
private Runtime() {}
}
这里还介绍了一种实现Serializable 的方式
Serialization
If the Singleton class implements the java.io.Serializable interface, when a singleton is serialized and then deserialized more than once, there will be multiple instances of Singleton created. In order to avoid this the readResolve method should be implemented. See Serializable () and readResolve Method () in javadocs.
public class Singleton implements Serializable {
...
// This method is called immediately after an object of this class is deserialized.
// This method returns the singleton instance.
protected Object readResolve() {
return getInstance();
}
}
网友评论