单例模式一定要扛得住并发。
单例模式有很多版本,包括双重检查,静态内部类,枚举,其他的写法也有,建议大家看深入浅出单实例SINGLETON设计模式。
饿汉单例的话太一劳永逸,如果你碰上了那种要使用很多资源的类的单例,但是一时半会儿又用不上的情况,那么还是不要用了。我写的单例都是使用的时候才加载的那种,也就是所谓的懒汉式单例。
单例模式的特点总结为两私有一公开,随时注意静态(我认为是两处静态)
,两私有
是指私有构造方法
,私有的指向自身的实例
,一公开
是指公开的对外获取单例的方法
,随时注意静态
是指:因为构造方法私有,所以你不能通过创建实例来调用方法,那么就只有通过类名来调用里面的静态方法,记忆的链式思维是这样的,获取单例的方法必须静态,这个方法要调用指向单例的实例,由于静态方法只能调用静态方法和静态变量,那么被调用的那个单例的实例也必须是静态的(多注意枚举那里)。
基本的模板
class Singleton {
private Singleton(){}
private static Singleton singleton = null;
public static Singleton getSingleton() {
return null;
}
}
具体的实现
/**
* Double check && volatile Edition
* 双重检查volatile版本
*/
class DoubleCheckSingleton {
private DoubleCheckSingleton(){}
private static volatile DoubleCheckSingleton INSTANCE = null;
public static DoubleCheckSingleton getSingleton() {
if (INSTANCE == null) {
synchronized (DoubleCheckSingleton.class) {
if (INSTANCE == null) {
INSTANCE = new DoubleCheckSingleton();
}
}
}
return INSTANCE;
}
}
/**
* Static inner class Edition
* 静态内部类版本
*/
class InnerClassSingleton {
private InnerClassSingleton(){}
private static class SingletonHolder {
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getSingleton() {
return SingletonHolder.INSTANCE;
}
}
/**
* Enum Edition
* 枚举版本
*/
class EnumSingleton {
private EnumSingleton(){}
public static EnumSingleton getSingleton() {
return singleton.INSTANCE.getSingleton();
}
enum singleton {
INSTANCE;
private EnumSingleton singleton;
private singleton(){ singleton = new EnumSingleton();}
private EnumSingleton getSingleton(){return singleton;}
}
}
/**
* 测试线程,在这里更改使用的版本,虽然这很不软件工程
*/
class SimpleThread implements Runnable {
@Override
public void run() {
System.out.println(DoubleCheckSingleton.getSingleton());
}
}
/**
* 测试主入口
*/
public class SingletonVersions {
public static void main(String[] args) {
SimpleThread simpleThread = new SimpleThread();
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(simpleThread);
thread.start();
}
}
}
不知道大家注意到了没有,我这里这么多个类,就SingletonVersions是public的,而且这么多个代码全在一个文件也就是SingletonVersions.java里面,没错,但是大家注意,访问权限,没有写public的类只具有包访问权限。一个java文件里面只准存在一个public的类,但是还可以写其他的非public类,还可以写接口,枚举,我经常因为代码很短,图方便的话就写在一个类里面了。
泛型单例
这个提的人很少,但是是有他的存在价值的,比如你有很多个工具类,都想用单例模式来创建唯一对象来减小开销,那么写个泛型就是有必要了的。
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created by Curry on 17/6/26.
*/
public class Singleton<T> {
/**
* 私有构造函数,在这里显得表鸡肋,没有任何作用
* 但是处于对单例模式的尊重,要给全它定义中的,私有构造函数、私有静态自身实例,共有静态的获取单例的方法
*/
private Singleton() {
}
/**
* 私有,静态,并发哈希表,用于存储所有的实例的唯一单例,而且其本身也是静态唯一的
*/
private static ConcurrentHashMap<Class, Object> map = new ConcurrentHashMap<>();
/**
* 公开,静态,泛型的获取对应类型的单例
*/
public static <T> T getDefault(Class<T> type) {
if (map.get(type) == null) {
synchronized (map) {
if (map.get(type) == null) {
try {
/**
* 这里利用反射,将私有的构造方法改为共有的,用于创建实例,否则无法创建实例
*/
Constructor constructor = type.getDeclaredConstructor();
constructor.setAccessible(true);
map.put(type, constructor.newInstance());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
return (T)map.get(type);
}
/**
* 根据类型移除存储在哈希表中的单例
*/
public static <T> void removeSingleton(Class<T> type) {
map.remove(type);
}
}
网友评论