单例模式可以避免多个对象对资源的消耗。
使用场景
当某类只需一个对象的时候,如:IO的访问;数据库的访问;图片加载ImageLoader。
为什么ImageLoader要使用单例?
ImageLoader中又含有线程池、缓存、网络请求等比较耗资源的对象,如果多次构建,则会消耗过多资源。
实现方式
1. 声明式:不推荐
private Test() {
}
public final static Test test = new Test();
优点:简单
缺点:粗暴(系统启动时初始化,对节约资源不够充分)
2. 懒汉式:不推荐
private Test() {
}
private static Test test;
public static synchronized Test getInstance() {
if (test == null) {
test = new Test();
}
return test;
}
优点:第一次使用时候初始化,保证线程安全
缺点:每次使用都会同步,性能差
3. DCL(Double Check Lock)方式:可用,最常用的方式。
private Test() {
}
private static Test test;
public static Test getInstance() {
if (test == null) {
synchronized (Test.class) {
if (test == null) {
test = new Test();
}
}
}
return test;
}
优点:第一次使用时初始化,保证线程安全,避免不必要的同步
缺点:高并发环境有缺陷
4. 静态内部类式:推荐。
private Test() {
}
public static Test getInstance() {
return InstanceHolder.test;
}
private static class InstanceHolder {
private final static Test test = new Test();
}
优点:延迟实例化,线程安全,单例唯一性。
缺点:class个数增加。
5. 枚举式
public enum Test {
Instance;
private int count;
public void countPlus(int plus) {
count += plus;
}
}
使用:Test.Instance.countPlus(3);
优点:线程安全,绝对唯一性(上述的几种方式,在反序列化情况下会重新创建对象)。
缺点:没有延迟加载。
6. 容器式
private static Map<String, Object> instanceMap = new HashMap<>();
public static void registerService(String key, Object instance) {
if (!instanceMap.containsKey(key)) {
instanceMap.put(key, instance);
}
}
public static Object getService(String key) {
return instanceMap.get(key);
}
系统启动时,将多种单例注入容器,使用时取出。
总结
单例的核心都是将构造函数私有化,通过静态方法获取唯一实例,尽量节约资源提高性能,保证线程安全,防止反序列化导致重新实例化。