引言

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