引言
在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: 每日一算法,由浅入深,欢迎加入一同共勉。