ThreadLocal:你不知道的优化技巧,Android开发者都在用
引言
在Android
开发中,多线程是一个常见的话题。为了有效地处理多线程的并发问题,Android
提供了一些工具和机制。其中,ThreadLocal
是一个强大的工具,它可以使得每个线程都拥有自己独立的变量副本,从而避免了线程安全问题。
本文将深入探讨Android中的ThreadLocal
原理及其使用技巧, 帮助你更好的理解和使用ThreadLocal
。
ThreadLocal的原理
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
对象保存的值。
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();
}
当我们调用ThreadLocal
的set()
方法时,会将值存储到当前线程的ThreadLocalMap
对象中。当我们调用ThreadLocal
的get()
方法时,会从当前线程的ThreadLocalMap
对象中获取值。
ThreadLocal的使用
使用ThreadLocal
非常简单,首先需要创建一个ThreadLocal
对象,然后通过set
和get
方法来设置和获取线程的局部变量。以下是一个简单的例子:
val threadLocal = ThreadLocal<String>()
fun setThreadName(name: String) {
threadLocal.set(name)
}
fun getThreadName(): String {
return threadLocal.get() ?: "DefaultThreadName"
}
在Android
开发中,ThreadLocal
的使用场景非常多,比如:
- 在
Activity
中存储Fragment
的状态 - 在
Handler
中存储消息的上下文 - 在
RecyclerView
中存储滚动位置
实际应用场景
// 在 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
变量时,显式地将其移除。
threadLocal.remove()
- 不适合全局变量:
ThreadLocal
适用于需要在线程间传递的局部变量,但不适合作为全局变量的替代品。
优化技巧
- 合理使用默认值: 在获取
ThreadLocal
值时,可以通过提供默认值来避免返回null
,确保代码的健壮性。
fun getThreadName(): String {
return threadLocal.get() ?: "DefaultThreadName"
}
- 懒加载初始化: 避免在声明
ThreadLocal
时就初始化,可以使用initialValue
方法进行懒加载,提高性能。
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: 每日一算法,由浅入深,欢迎加入一起共勉。
来源:juejin.cn/post/7317859658285858842