ThreadLocal:你不知道的优化技巧,Android开发者都在用

引言

Android开发中,多线程是一个常见的话题。为了有效地处理多线程的并发问题,Android提供了一些工具和机制。其中,ThreadLocal是一个强大的工具,它可以使得每个线程都拥有自己独立的变量副本,从而避免了线程安全问题。

本文将深入探讨Android中的ThreadLocal原理及其使用技巧, 帮助你更好的理解和使用ThreadLocal

ThreadLocal的原理

java 复制代码
public class Thread implements Runnable {

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
}

ThreadLocal的原理是基于每个线程都有一个独立的ThreadLocalMap对象。ThreadLocalMap对象是一个Map,它的键是ThreadLocal对象,值是ThreadLocal对象保存的值。

ini 复制代码
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        map.set(this, value);
    } else {
        createMap(t, value);
    }
}

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

当我们调用ThreadLocalset()方法时,会将值存储到当前线程的ThreadLocalMap对象中。当我们调用ThreadLocalget()方法时,会从当前线程的ThreadLocalMap对象中获取值。

ThreadLocal的使用

使用ThreadLocal非常简单,首先需要创建一个ThreadLocal对象,然后通过setget方法来设置和获取线程的局部变量。以下是一个简单的例子:

kotlin 复制代码
val threadLocal = ThreadLocal<String>()

fun setThreadName(name: String) {
    threadLocal.set(name)
}

fun getThreadName(): String {
    return threadLocal.get() ?: "DefaultThreadName"
}

Android开发中,ThreadLocal的使用场景非常多,比如:

  • Activity中存储Fragment的状态
  • Handler中存储消息的上下文
  • RecyclerView中存储滚动位置

实际应用场景

kotlin 复制代码
// 在 Activity 中存储 Fragment 的状态
class MyActivity : AppCompatActivity() {

    private val mFragmentState = ThreadLocal<FragmentState>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_my)

        // 获取 Fragment 的状态
        val fragmentState = mFragmentState.get()
        if (fragmentState == null) {
            // 初始化 Fragment 的状态
            fragmentState = FragmentState()
        }

        // 设置 Fragment 的状态
        mFragmentState.set(fragmentState)

        // 创建 Fragment
        val fragment = MyFragment()
        fragment.arguments = fragmentState.toBundle()
        supportFragmentManager.beginTransaction().add(R.id.container, fragment).commit()
    }

}

class FragmentState {

    var name: String? = null
    var age: Int? = null

    fun toBundle(): Bundle {
        val bundle = Bundle()
        bundle.putString("name", name)
        bundle.putInt("age", age)
        return bundle
    }

}

这段代码在Activity中使用ThreadLocal来存储Fragment的状态。当Activity第一次启动时,会初始化Fragment的状态。当Activity重新启动时,会从ThreadLocal中获取Fragment的状态,并将其传递给Fragment

注意事项

  • 内存泄漏风险:

ThreadLocal变量的生命周期与线程的生命周期是一致的。这意味着,如果一个线程一直不结束,那么它所持有的ThreadLocal变量也不会被释放。这可能会导致内存泄漏。

为了避免内存泄漏,我们应该在不再需要ThreadLocal变量时,显式地将其移除。

kotlin 复制代码
threadLocal.remove()
  • 不适合全局变量: ThreadLocal适用于需要在线程间传递的局部变量,但不适合作为全局变量的替代品。

优化技巧

  • 合理使用默认值: 在获取ThreadLocal值时,可以通过提供默认值来避免返回null,确保代码的健壮性。
kotlin 复制代码
fun getThreadName(): String {
    return threadLocal.get() ?: "DefaultThreadName"
}
  • 懒加载初始化: 避免在声明ThreadLocal时就初始化,可以使用initialValue方法进行懒加载,提高性能。
kotlin 复制代码
val threadLocal = object : ThreadLocal<String>() {
    override fun initialValue(): String {
        return "DefaultValue"
    }
}
  • 尽量避免在ThreadLocal中保存大对象

结论

在本文中,我们介绍了ThreadLocal的原理和使用技巧,希望这些知识能够帮助你更好地理解和使用它。

推荐

android_startup: 提供一种在应用启动时能够更加简单、高效的方式来初始化组件,优化启动速度。不仅支持Jetpack App Startup的全部功能,还提供额外的同步与异步等待、线程控制与多进程支持等功能。

AwesomeGithub: 基于Github的客户端,纯练习项目,支持组件化开发,支持账户密码与认证登陆。使用Kotlin语言进行开发,项目架构是基于JetPack&DataBinding的MVVM;项目中使用了Arouter、Retrofit、Coroutine、Glide、Dagger与Hilt等流行开源技术。

flutter_github: 基于Flutter的跨平台版本Github客户端,与AwesomeGithub相对应。

android-api-analysis: 结合详细的Demo来全面解析Android相关的知识点, 帮助读者能够更快的掌握与理解所阐述的要点。

daily_algorithm: 每日一算法,由浅入深,欢迎加入一起共勉。

相关推荐
iOS阿玮1 分钟前
苹果 iOS 19 曝光,你的iPhone 还能再战一年?
app·apple
鸿蒙布道师2 分钟前
鸿蒙NEXT开发对象工具类(TS)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
庸俗今天不摸鱼16 分钟前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
黄毛火烧雪下23 分钟前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox34 分钟前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞36 分钟前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行36 分钟前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_5937581038 分钟前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox
掘金一周41 分钟前
金石焕新程 >> 瓜分万元现金大奖征文活动即将回归 | 掘金一周 4.3
前端·人工智能·后端
三翼鸟数字化技术团队1 小时前
Vue自定义指令最佳实践教程
前端·vue.js