单例模式-系统只有一个实例

 在基金业务中,基金数量非常多,查询起来非常慢,优化方案是系统每天从数据库把所有的基金信息取出并缓存起来,用户查询的时候直接从缓存里取。而基金缓存信息每天都需要定时更新。

传统的技术方案如下:

图 传统技术方案

上述方案有个严重问题,就是FundCacheUtil可以被创建多个实例,这样既会给系统加重负担,同时在不同的FundCacheUtil的实例中,获取的基金信息可能会不一致。

当然,也可以把FundCacheUtil的cacheList设置为静态变量,其方法都设置为静态方法。这样,就能保证系统只存在一个cacheList了。

public class FundCacheUtils {

    private static List<Map<String,Object>> cacheList;

    private FundCacheUtils() {}

    private static void updateCache() {
        //每天7点定时更新
        cacheList = new ArrayList<>();
    }

    public static List<Map<String,Object>> getCacheList() {
        synchronized (FundCacheUtils.class) {
            if (cacheList == null) {
                updateCache();
            }
        }
        return cacheList;
    }

}

public class Client {

    public List<Map<String,Object>> searchFundList() {
        return FundCacheUtils.getCacheList();
    }

    public static void main(String[] args) {
        Client client = new Client();
        List<Map<String, Object>> list1 = client.searchFundList();
        List<Map<String, Object>> list2 = client.searchFundList();
        System.out.println(list1 == list2);
    }

}

这段代码虽然保证了系统只存在一个cacheList,但也存在一个问题:跟新缓存(updateCache)的操作不应该出现在getCacheList中,而是需要确保在调用getCacheList方法之前,已经执行完了updateCache方法。

1 单例模式概述

确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。

图 单例模式

Singleton: 单例类,只有一个实例。对外提供getInstance()方法来向整个系统提供这个实例。

public class FundCacheStorage {

    volatile private static FundCacheStorage instance = null;

    private List<Map<String,Object>> cacheList;

    private FundCacheStorage() {
        updateCacheList();
    }

    private void updateCacheList() {
        // 每天7点执行一次
        cacheList = new ArrayList<>();
    }

    public List<Map<String,Object>> getCacheList() {
        return this.cacheList;
    }

    public static FundCacheStorage getInstance() {
        synchronized (FundCacheStorage.class) {
            if (instance == null) instance = new FundCacheStorage();
        }
        return instance;
    }

}

public class Client {

    public static void main(String[] args) {
        FundCacheStorage instance1 = FundCacheStorage.getInstance();
        List<Map<String, Object>> cacheList1 = instance1.getCacheList();

        FundCacheStorage instance2 = FundCacheStorage.getInstance();
        List<Map<String, Object>> cacheList2 = instance2.getCacheList();

        System.out.println(cacheList2 == cacheList1);
    }

}

1.1 饿汉式与懒汉式

饿汉式:定义静态变量的时候实例化单例类,在类加载时自行实例化。

private static Singleton instance = new Singleton();

懒汉式:在类加载时并不自行实例化,在需要的时候再加载实例。这种技术又称为延迟加载(Lazy Load)技术。

懒汉式获取实例的时候,需要使用双重检测锁定,来避免多线程会创建不同的实例。

模式

优点

缺点

饿汉式

  1. 无须考虑多线程访问问题,可以确保实例的唯一性。
  2. 从调用速度和反应时间角度来讲,效率优先于懒汉式。
  1. 在类加载时该对象就需要创建,从资源利用效率角度来讲,劣于懒汉式。
  2. 在系统加载时需要创建饿汉式单例对象,加载时间可能会比较长。

懒汉式

1)在第一次使用时才创建,不会一直占用系统资源。

1)必须处理好多线程同时访问问题。

表 饿汉式与懒汉式优缺点

1.2 按需初始化持有者(IoDH)

全称为Initialization on Demand Holder,简称为IoDh技术,实现IoDH时,需在单例类中增加一个静态内部类,在该内部内中创建实例对象,再将该实例对象通过getInstance()方法返回给外部使用。

public class FundCacheStorage2 {

    private List<Map<String,Object>> cacheList;

    private FundCacheStorage2() {}

    private static class HolderClass {
        private final static FundCacheStorage2 instance = new FundCacheStorage2();
    }

    public static FundCacheStorage2 getInstance() {
        return HolderClass.instance;
    }

    private void updateCacheList() {
        // 每天7点执行一次
        cacheList = new ArrayList<>();
    }

    public List<Map<String,Object>> getCacheList() {
        return this.cacheList;
    }

}

IoDH 技术由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。由于getInstance()方法没有被任何线程锁定,因此其性能不会造成任何影响。

通过使用IoDH,既可以实现延迟加载,又可以保证线程安全,不影响系统性能。因此,IoDH不失为一种最好的Java语言单例模式的实现方式。但是该技术与编程语言本身的特性相关,很多面向对象语言不支持IoDH.

2 优缺点

优点

  1. 单例模式提供了对唯一实例的受控访问。它可以严格控制客户怎样以及何时访问它。
  2. 系统内存中只存在一个对象,可以节约系统资源。

3)允许可变数目的实例,基于单例模式,开发者可进行扩展,来获得指定个数的实例对象。即节省系统资源,又解决了由于单例对象共享过多有损性能的问题。

缺点

  1. 单例模式职责过重,既提供了业务方法,又提供了创建对象的方法。
  2. 如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自定销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态丢失。

3 适用场景

  1. 系统只需一个实例对象。
  2. 实例对象需要在系统中提供共享内容。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值