首页 > 其他分享 >Launcher3 中 IconCache 缓存逻辑

Launcher3 中 IconCache 缓存逻辑

时间:2023-09-15 18:39:01浏览次数:41  
标签:IconCache 缓存 final new entry Launcher3 public 图标

概述

Launcher3 中 IconCache 缓存逻辑 _Android

我们先看下IconCache的初始化过程,接着看下IconCache核心数据结构、算法,最后介绍与之关联的几个类。

Launcher.java

public class Launcher extends StatefulActivity<LauncherState> implements ... {
    ...
    public static final String TAG = "Launcher";
    private LauncherModel mModel;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        LauncherAppState app = LauncherAppState.getInstance(this);
        mOldConfig = new Configuration(getResources().getConfiguration());
        mModel = app.getModel();
        ...
       }
   }
  • 这个类是Launcher的主入口,即 MainActivity。onCreate()做了许多界面和管理器的初始化。
  • 这里我们关注是初始化了 LauncherAppStateLauncherModel

LauncherAppState.java

public class LauncherAppState {
    // 注释1
    // We do not need any synchronization for this variable as its only written on UI thread.
    public static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
            new MainThreadInitializedObject<>(LauncherAppState::new);

    private final Context mContext;
    private final LauncherModel mModel;
    private final IconProvider mIconProvider;
    private final IconCache mIconCache;
    private final DatabaseWidgetPreviewLoader mWidgetCache;
    private final InvariantDeviceProfile mInvariantDeviceProfile;
    private final RunnableList mOnTerminateCallback = new RunnableList();

    public static LauncherAppState getInstance(final Context context) {
        return INSTANCE.get(context);
    }

    public LauncherAppState(Context context) {
        // 注释2
        this(context, LauncherFiles.APP_ICONS_DB);
        ...
    }

   public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
       ...
       // 注释3
       mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
       iconCacheFileName, mIconProvider);
       ...
   }
}
  • 注释1:LauncherAppState 是单例,且限定在主线程上初始化
  • 注释2 注释3 传入数据库名字 app_icon.db,进而初始化 IconCache
  • mModel 即数据管理器,用于维护启动器的内存状态。预计静态中应该只有一个LauncherModel对象。还提供用于更新 Launcher 的数据库状态的 API。
  • mIconCache应用程序icon和title的缓存,图标可以由任何线程创建。
  • mWidgetCache 存储widget预览信息的数据库

LoaderTask.java

是有数据管理类LauncherModel来调用的,其核心是Run方法。

Launcher3 中 IconCache 缓存逻辑 _ide_02

主要分为四大步骤,并开启事务机制来管理

加载与绑定桌面内容

  1. loadWorkspace
  2. sanitizeData
  3. bindWorkspace
  4. sendFirstScreenActiveInstallsBroadcast

加载和绑定所有的应用图标和信息

  1. loadAllApps
  2. bindAllApps
  3. update icon cache 对应图标缓存逻辑类 LauncherActivityCachingLogic
  4. save shortcuts in icon cache

这一步实际是在第一步的,对应的图标缓存逻辑类 ShortcutCachingLogic

加载和绑定所有DeepShortcuts

  1. loadDeepShortcuts
  2. bindDeepShortcuts
  3. save deep shortcuts in icon cache 对应的图标缓存逻辑类 ShortcutCachingLogic

加载和绑定所有的Widgets

  1. load widgets
  2. bindWidgets
  3. save widgets in icon cache 对应的图标缓存逻辑类 ComponentWithIconCachingLogic

IconCacheUpdateHandler.java

IconCacheUpdateHandler扫描到所有应用后,会开启一个线程 SerializedIconUpdateTask进行更新图标操作,把图标缓存到内存和数据库里。

调用流程

Launcher3 中 IconCache 缓存逻辑 _缓存_03

  • 在上面LoaderTask过程中更新图标用的是IconCacheUpdateHandler.updateIcons()
  • 这是个工具类,处理更新图标缓存, 处理业务与IconCache的连接
  • 内部类 SerializedIconUpdateTask 序列化图标更新任务,即将这些图标信息存储或者更新到数据库中

举例说明过程

Launcher3 中 IconCache 缓存逻辑 _Android_04

updateIcons

public <T> void updateIcons(List<T> apps, CachingLogic<T> cachingLogic,
        OnUpdateCallback onUpdateCallback) {
    // Filter the list per user
    HashMap<UserHandle, HashMap<ComponentName, T>> userComponentMap = new HashMap<>();
    int count = apps.size();
    for (int i = 0; i < count; i++) {
        T app = apps.get(i);
        UserHandle userHandle = cachingLogic.getUser(app);
        HashMap<ComponentName, T> componentMap = userComponentMap.get(userHandle);
        if (componentMap == null) {
            componentMap = new HashMap<>();
            userComponentMap.put(userHandle, componentMap);
        }
        componentMap.put(cachingLogic.getComponent(app), app);
    }

    for (Entry<UserHandle, HashMap<ComponentName, T>> entry : userComponentMap.entrySet()) {
        updateIconsPerUser(entry.getKey(), entry.getValue(), cachingLogic, onUpdateCallback);
    }

    // From now on, clear every valid item from the global valid map.
    mFilterMode = MODE_CLEAR_VALID_ITEMS;
}
  • 这里有两个Map,按照用户维度来分组组件
  • 按照用户维度来分组组件 HashMap<UserHandle, HashMap<ComponentName, T>> userComponentMap;
  • 按照组件不同分组 HashMap<ComponentName, T> componentMap = userComponentMap.get(userHandle);

updateIconsPerUser


/**
 * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in
 * the DB and are updated.
 * @return The set of packages for which icons have updated.
 */
@SuppressWarnings("unchecked")
private <T> void updateIconsPerUser(UserHandle user, HashMap<ComponentName, T> componentMap,
        CachingLogic<T> cachingLogic, OnUpdateCallback onUpdateCallback) {
    Set<String> ignorePackages = mPackagesToIgnore.get(user);
    if (ignorePackages == null) {
        ignorePackages = Collections.emptySet();
    }
    long userSerial = mIconCache.getSerialNumberForUser(user);
    Log.d(TAG, "updateIconsPerUser: userSerial = " + userSerial + " ,componentMap =" + componentMap.size());

    Stack<T> appsToUpdate = new Stack<>();
    try (Cursor c = mIconCache.mIconDb.query(
            new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT,
                    IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION,
                    IconDB.COLUMN_SYSTEM_STATE},
            IconDB.COLUMN_USER + " = ? ",
            new String[]{Long.toString(userSerial)})) {

        final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT);
        final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED);
        final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION);
        final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID);
        final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE);

        Log.d(TAG, "updateIconsPerUser: 111");
        while (c.moveToNext()) {
            Log.d(TAG, "updateIconsPerUser: 222");
            ...
        }
    } catch (SQLiteException e) {
        Log.d(TAG, "Error reading icon cache", e);
        // Continue updating whatever we have read so far
    }

    // Insert remaining apps.
    if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) {
        Stack<T> appsToAdd = new Stack<>();
        appsToAdd.addAll(componentMap.values());
        Log.d(TAG, "SerializedIconUpdateTask appsToAdd = " + appsToAdd.size() + ", appsToUpdate = "+ appsToUpdate.size());
        new SerializedIconUpdateTask(userSerial, user, appsToAdd, appsToUpdate, cachingLogic,
                onUpdateCallback).scheduleNext();
    }
}
  • 为什么要删除操作?setIgnorePackages

SerializedIconUpdateTask.run()

private class SerializedIconUpdateTask<T> implements Runnable {
    ....
    @Override
    public void run() {
       ...
       if (!mAppsToAdd.isEmpty()) {
            T app = mAppsToAdd.pop();
            PackageInfo info = mPkgInfoMap.get(mCachingLogic.getComponent(app).getPackageName());
            // We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every
            // app should have package info, this is not guaranteed by the api
            if (info != null) {
                mIconCache.addIconToDBAndMemCache(app, mCachingLogic, info,
                        mUserSerial, false /*replace existing*/);
            }

            if (!mAppsToAdd.isEmpty()) {
                scheduleNext();
            }
        }
    }

    public void scheduleNext() {
        mIconCache.mWorkerHandler.postAtTime(this, ICON_UPDATE_TOKEN,
                SystemClock.uptimeMillis() + 1);
    }
}

IconCache.java

核心思想:针对每类图标提供通用的HashMap内存缓存 + 数据库缓存,同时通过CachingLogic多种实现图标差异性。


// 加载 shortcut 图标
private synchronized <T extends ItemInfoWithIcon> void getShortcutIcon(T info, ShortcutInfo si,
        boolean useBadged, @NonNull Predicate<T> fallbackIconCheck) {
    BitmapInfo bitmapInfo;
    if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
        bitmapInfo = cacheLocked(ShortcutKey.fromInfo(si).componentName, si.getUserHandle(),
                () -> si, mShortcutCachingLogic, false, false).bitmap;
    } else {
        // If caching is disabled, load the full icon
        bitmapInfo = mShortcutCachingLogic.loadIcon(mContext, si);
    }
    if (bitmapInfo.isNullOrLowRes()) {
        bitmapInfo = getDefaultIcon(si.getUserHandle());
    }

    if (isDefaultIcon(bitmapInfo, si.getUserHandle()) && fallbackIconCheck.test(info)) {
        return;
    }
    info.bitmap = bitmapInfo;
    if (useBadged) {
        BitmapInfo badgeInfo = getShortcutInfoBadge(si);
        try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
            info.bitmap = li.badgeBitmap(info.bitmap.icon, badgeInfo);
        }
    }
}

/**
 * 加载 Widget 图标
 */
public synchronized String getTitleNoCache(ComponentWithLabel info) {
    CacheEntry entry = cacheLocked(info.getComponent(), info.getUser(), () -> info,
            mComponentWithLabelCachingLogic, false /* usePackageIcon */,
            true /* useLowResIcon */);
    return Utilities.trim(entry.title);
}

BaseIconCache.java

1.首先看下构造方法
public abstract class BaseIconCache {
    ....
    private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
    private final Map<ComponentKey, CacheEntry> mCache;
    ...
    public BaseIconCache(Context context, String dbFileName, Looper bgLooper,
        int iconDpi, int iconPixelSize, boolean inMemoryCache) {
        ...
        if (inMemoryCache) {
            mCache = new HashMap<>(INITIAL_ICON_CACHE_CAPACITY);
        } else {
            // Use a dummy cache
            mCache = new AbstractMap<ComponentKey, CacheEntry>() {
                @Override
                public Set<Entry<ComponentKey, CacheEntry>> entrySet() {
                    return Collections.emptySet();
                }

                @Override
                public CacheEntry put(ComponentKey key, CacheEntry value) {
                    return value;
                }
            };
        }
        ...
    }

}

// 缓存key, 组成: 组件名 和 用户UserHandle
public class ComponentKey {

    public final ComponentName componentName;
    public final UserHandle user;

    private final int mHashCode;

    public ComponentKey(ComponentName componentName, UserHandle user) {
        if (componentName == null || user == null) {
            throw new NullPointerException();
        }
        this.componentName = componentName;
        this.user = user;
        mHashCode = Arrays.hashCode(new Object[] {componentName, user});
    }
    ...
}

// 缓存Value,组成:图标 + title + contentDesc
public static class CacheEntry {

    @NonNull
    public BitmapInfo bitmap = BitmapInfo.LOW_RES_INFO;
    public CharSequence title = "";
    public CharSequence contentDescription = "";
}
  • 缓存数据结构:Map<ComponentKey, CacheEntry>
  • 缓存集合初始大小为50
  • ComponentKey:缓存key, 组成: 组件名(pkg+cls) 和 用户UserHandle
  • CacheEntry:缓存Value,组成:图标 + title + contentDesc
  • 注意这里有一段代码是虚内存,使用技巧值得学习
// Use a dummy cache
            mCache = new AbstractMap<ComponentKey, CacheEntry>() {
                @Override
                public Set<Entry<ComponentKey, CacheEntry>> entrySet() {
                    return Collections.emptySet();
                }

                @Override
                public CacheEntry put(ComponentKey key, CacheEntry value) {
                    return value;
                }
            };
2.继续看另一个重要方法 cacheLocked()
/**
 * @param  componentName  组件名
 * @param  user 用户
 * @param  infoProvider 组件信息提供者
 * @param  cachingLogic  对应的缓存逻辑处理类
 * @param  usePackageIcon 是否使用pkg的icon
 * @param  useLowResIcon  是否使用默认的空图标
 */
protected <T> CacheEntry cacheLocked(
        @NonNull ComponentName componentName, @NonNull UserHandle user,
        @NonNull Supplier<T> infoProvider, @NonNull CachingLogic<T> cachingLogic,
        boolean usePackageIcon, boolean useLowResIcon) {
    assertWorkerThread();
    // 1.生成缓存key
    ComponentKey cacheKey = new ComponentKey(componentName, user);
    // 2.尝试根据key,从缓存中取
    CacheEntry entry = mCache.get(cacheKey);
    // 3.尚未缓存 或者 缓存了但是缓存的是空的默认图标,此时去缓存
    if (entry == null || (entry.bitmap.isLowRes() && !useLowResIcon)) {
        entry = new CacheEntry();
        //4.如果对应的缓存逻辑控制类 允许添加到内存缓存中,即存入mCache,但此时value未赋值
        if (cachingLogic.addToMemCache()) { 
            mCache.put(cacheKey, entry);
        }

        // Check the DB first.
        T object = null;
        boolean providerFetchedOnce = false;

        // 4.首先查看数据库是否存在
        // 如果数据存在,取出来赋值给entry
        // 如果数据库不存在,加载默认空图标、pkg图标、或者 cachingLogic.loadIcon
        if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
            object = infoProvider.get();
            providerFetchedOnce = true;

            if (object != null) { // 4.1如果信息提供者不为空,直接去对应的缓存控制逻辑取图标
                entry.bitmap = cachingLogic.loadIcon(mContext, object);
            } else { // 4.2如果提供者是空的,返回默认的或者使用pkg的图标
                if (usePackageIcon) {
                    CacheEntry packageEntry = getEntryForPackageLocked(
                            componentName.getPackageName(), user, false);
                    if (packageEntry != null) {
                        if (DEBUG) Log.d(TAG, "using package default icon for " +
                                componentName.toShortString());
                        entry.bitmap = packageEntry.bitmap;
                        entry.title = packageEntry.title;
                        entry.contentDescription = packageEntry.contentDescription;
                    }
                }
                // 如果pkg依然为空,使用默认的空白图标
                if (entry.bitmap == null) {
                    if (DEBUG) Log.d(TAG, "using default icon for " +
                            componentName.toShortString());
                    entry.bitmap = getDefaultIcon(user);
                }
            }
        }

        // 5.检查并对entry的title和desc继续赋值
        if (TextUtils.isEmpty(entry.title)) {
            if (object == null && !providerFetchedOnce) {
                object = infoProvider.get();
                providerFetchedOnce = true;
            }
            if (object != null) {
                entry.title = cachingLogic.getLabel(object);
                entry.contentDescription = mPackageManager.getUserBadgedLabel(
                        cachingLogic.getDescription(object, entry.title), user);
            }
        }
    }
    return entry; // 返回缓存的Value,及CacheEntry
}

补充说明两点

  • getEntryFromDB 从数据库中查询目标 Entry
  • getEntryForPackageLocked 与上面这个方法类似,唯一多的逻辑是当从packagemanger查询到应用图标会存入到数据库
3.方法addIconToDBAndMemCache
/**
* 在数据库和内存缓存中添加一个条目。 
* @param replaceExisting 如果为真,它会重新创建位图,即使它已经存在于内存中。
* 这在以前的位图是使用旧数据创建时很有用。
*/
public synchronized <T> void addIconToDBAndMemCache(T object, CachingLogic<T> cachingLogic,
        PackageInfo info, long userSerial, boolean replaceExisting) {
    UserHandle user = cachingLogic.getUser(object);
    ComponentName componentName = cachingLogic.getComponent(object);

    final ComponentKey key = new ComponentKey(componentName, user);
    CacheEntry entry = null;
    if (!replaceExisting) {
        entry = mCache.get(key);
        // We can't reuse the entry if the high-res icon is not present.
        if (entry == null || entry.bitmap.isNullOrLowRes()) {
            entry = null;
        }
    }
    // 新加载图标
    if (entry == null) {
        entry = new CacheEntry();
        entry.bitmap = cachingLogic.loadIcon(mContext, object);
    }

    // 无法从 cachingLogic 加载图标,这意味着已加载替代图标(例如后备图标、默认图标)。
    // 所以我们放在这里,因为缓存空条目没有意义。
    if (entry.bitmap.isNullOrLowRes()) return;
    entry.title = cachingLogic.getLabel(object);
    entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
    // 是否需要添加到内存中
    if (cachingLogic.addToMemCache()) mCache.put(key, entry);

    ContentValues values = newContentValues(entry.bitmap, entry.title.toString(),
            componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList));
    // 添加到数据库
    addIconToDB(values, componentName, info, userSerial,
            cachingLogic.getLastUpdatedTime(object, info));
}

IconDB

  • 类路径:com.android.launcher3.icons.cache.BaseIconCache.IconDB
  • db数据库名:app_icons.db
  • table表名:icons

Launcher3 中 IconCache 缓存逻辑 _launcher3_05

某个手机数据库表示例

CachingLogic.java 系列

Launcher3 中 IconCache 缓存逻辑 _缓存_06

  • LauncherActivityCachingLogic 用于allApp的图标缓存
  • ShortcutCachingLogic 用于shortcut的图标缓存
  • ComponentWithIconCachingLogic 用于widget的图标缓存
  • 其中 loadIcon 是可以定制图标样式的
public class ShortcutCachingLogic implements CachingLogic<ShortcutInfo> {

    private static final String TAG = "ShortcutCachingLogic";

    // 根据shortcutInfo获取组件
    @Override
    public ComponentName getComponent(ShortcutInfo info) {
        return ShortcutKey.fromInfo(info).componentName;
    }

   ...

    @NonNull
    @Override
    public BitmapInfo loadIcon(Context context, ShortcutInfo info) {
        try (LauncherIcons li = LauncherIcons.obtain(context)) {
            Drawable unbadgedDrawable = ShortcutCachingLogic.getIcon(
                    context, info, LauncherAppState.getIDP(context).fillResIconDpi);
            if (unbadgedDrawable == null) return BitmapInfo.LOW_RES_INFO;
            return new BitmapInfo(li.createScaledBitmapWithoutShadow(
                    unbadgedDrawable, 0), Themes.getColorAccent(context));
        }
    }

    @Override
    public boolean addToMemCache() {
        return false;// 表示不缓存到内存中
    }

    /**
     * Similar to {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)} with additional
     * Launcher specific checks
     */
    public static Drawable getIcon(Context context, ShortcutInfo shortcutInfo, int density) {
        if (GO_DISABLE_WIDGETS) { // 开关控制是否允许有shortcut
            return null;
        }
        try {// 从LauncherApps中查询图标
            return context.getSystemService(LauncherApps.class)
                    .getShortcutIconDrawable(shortcutInfo, density);
        } catch (SecurityException | IllegalStateException e) {
            Log.e(TAG, "Failed to get shortcut icon", e);
            return null;
        }
    }
}

WidgetsModel.java

// True is the widget support is disabled.
public static final boolean GO_DISABLE_WIDGETS = true;
  • 当打开GO_DISABLE_WIDGETS = false ,会开启widget,同时在optionsview上会显示菜单, 如下图
OptionsPopupView.java
public static WidgetsFullSheet openWidgets(Launcher launcher) {
    if (launcher.getPackageManager().isSafeMode()) {
        Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
        return null;
    } else {
        return WidgetsFullSheet.show(launcher, true /* animated */);
    }
}

Launcher3 中 IconCache 缓存逻辑 _Android_07

  • 异常情况默认图标兜底 makeDefaultIcon com.android.launcher3.icons.BaseIconFactory#getFullResDefaultActivityIcon

标签:IconCache,缓存,final,new,entry,Launcher3,public,图标
From: https://blog.51cto.com/u_16175630/7484499

相关文章

  • redis批量先查缓存再查数据库
    RedisUtil:/***批量查询缓存,若是缓存没有的数据再调用对应的方法查询数据,查询之后放入缓存*@paramprefix缓存前缀*@paramparams缓存参数*@paramcolumn缓存参数对应字段列名*@paramdataBaseFunction数据库查询方法*@return......
  • 如何通过缓存提高后端应用程序性能
    性能是每个后端开发人员关心的问题。当应用程序需要频繁访问数据库或执行复杂的计算时,性能问题可能会显著影响用户体验。在这种情况下,缓存是一种常见的性能优化技术,它可以大大减少对数据库或其他资源的访问次数,从而提高响应时间和整体性能。缓存基础知识在深入讨论Java中的缓存实......
  • 本地缓存和Redis缓存
    Redis可以实现分布式的缓存,Map属于本地缓存,只能存在创建它的程序里Redis的缓存可以持久化,Map是内存对象,程序一重启数据就没了Redis缓存有过期机制,Map本身无此功能Redis可以处理每秒百万级的并发,是专业的缓存服务,Map只是一个普通的对象Redis可以用几十G内存来做缓存......
  • app测试日常踩坑——新老版本共用缓存问题
    需求:在测一个页面,老版本就存在的一个页面(多个模块,一个接口分成三次请求返回),但是新版本要新增一个模块。老的页面的实现方式是页面直接读取redis数据(还有其他逻辑,方便解释暂时这样说),截取模块的方式来分接口返回,新增的一个模块是要放在中间。方案:方案是沿用老的redis缓存,在老得缓存......
  • app测试日常踩坑——gocache缓存的过期时间和生成间隔问题
    问题:自动化监控平台添加的分类详情页的接口报错,分类详情页校验失败,看到的错误信息是接口响应错误,信息如下:{"errors":{"id":"0","code":"44010102","level":"1","status":"200","title":"参数错误","popup......
  • Docker 深度清除镜像缓存 (overlay2)
    Docker深度清除镜像缓存(overlay2)一般情况下,运维清理镜像是通过命令dockerrmi删除镜像的。但是这条命令不会删除dockerbuild命令产生的缓存文件。这个时候需要使用dockersystem的系列命令来做相关处理。dockersystem--help输出:#输出Usage:dockersystemCO......
  • 应对CDN缓存的一个小技巧
    工作中把一个文件放在CDN上面实时更新,并提供http下载地址。但是由于CDN缓存,下载下来的文件其实是几个小时以前的了。解决方法:下载请求的某尾添加一个随机参数,举例如:http://xxx.cdn.com/abc/123/test.xml?timestamp=12345678其中http://xxx.cdn.com/abc/123/test.xml为该文件的真实......
  • 基于HTTP协议的客户端缓存
    提到缓存,一般都会想起redis、memcached等分布式缓存系统。虽然使用这些服务器端缓存速度能提升不少,但其实还是需要客户端下载这部分数据,目前在移动互联网领域,节省流量是很有必要的,因此工作中也就需要加入客户端缓存的相关技术。 怎么使用客户端缓存?1、用http报头中的last-modified......
  • 146. LRU 缓存
    请你设计并实现一个满足LRU(最近最少使用)缓存约束的数据结构。实现LRUCache类:LRUCache(intcapacity)以正整数作为容量capacity初始化LRU缓存intget(intkey)如果关键字key存在于缓存中,则返回关键字的值,否则返回-1。voidput(intkey,intvalue)如果......
  • 关于缓存系统的mutex模式实现
    这篇blog是在上一家互联网公司某产品开发过程中的一些经验总结,整理一下分享上来。 关于mutex模式:对于高并发大访问量的应用,一般都会在数据库访问前加一层缓存系统。但是如果某一时刻某个缓存的key失效,而reload该key缓存的时间又比较长,导致大量的请求直接访问数据库,则会直接将数据......