注册

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();
}

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


ThreadLocal的使用


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


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

0 个评论

要回复文章请先登录注册