MST

星途 面试题库

面试题:Kotlin Lambda表达式与闭包在多线程场景下的应用与挑战

在多线程环境中,Kotlin的Lambda表达式和闭包可能会带来哪些潜在问题,例如资源竞争、内存泄漏等。针对这些问题,你有哪些解决方案?请结合具体的多线程场景(如并发集合操作)给出详细的分析和代码示例。
38.9万 热度难度
编程语言Kotlin

知识考点

AI 面试

面试题答案

一键面试

潜在问题

  1. 资源竞争
    • 分析:Kotlin的Lambda表达式和闭包可以访问外部作用域的变量。在多线程环境下,多个线程同时访问和修改这些共享变量时,就可能发生资源竞争。例如,在并发集合操作中,如果一个Lambda表达式在向集合中添加元素,同时另一个线程也在对该集合进行读取或修改操作,可能导致数据不一致。
    • 示例
import java.util.concurrent.CopyOnWriteArrayList

val sharedList = CopyOnWriteArrayList<Int>()
val threads = (1..10).map {
    Thread {
        (1..10).forEach {
            sharedList.add(it)
        }
    }
}
threads.forEach { it.start() }
threads.forEach { it.join() }
println(sharedList)

在上述代码中,如果sharedList不是线程安全的集合(如CopyOnWriteArrayList),多个线程同时添加元素就可能导致资源竞争问题。

  1. 内存泄漏
    • 分析:如果Lambda表达式或闭包持有对外部对象的引用,并且该外部对象的生命周期应该比Lambda表达式短,但由于Lambda表达式被某个长时间运行的线程或对象持有,就可能导致外部对象无法被垃圾回收,从而造成内存泄漏。例如,在Android开发中,如果一个Activity中的Lambda表达式被一个后台线程持有,而Activity已经销毁,但由于Lambda表达式的引用,Activity及其相关资源无法被回收。
    • 示例
class OuterClass {
    val innerLambda: () -> Unit
    init {
        innerLambda = {
            println("This is a lambda in OuterClass")
        }
    }
}
fun main() {
    val outer = OuterClass()
    val thread = Thread {
        outer.innerLambda()
    }
    // 如果这里没有对thread进行合适的管理,比如thread一直运行,而outer对象本应被回收,就可能造成内存泄漏
}

解决方案

  1. 资源竞争解决方案
    • 使用线程安全的数据结构:如上述代码中使用CopyOnWriteArrayList,它在每次修改操作时都会创建一个新的底层数组副本,读操作则基于旧的数组,从而保证线程安全。
    • 同步机制:使用synchronized关键字或ReentrantLock等同步工具。
    • 示例(使用synchronized
val sharedList2 = mutableListOf<Int>()
val threads2 = (1..10).map {
    Thread {
        synchronized(sharedList2) {
            (1..10).forEach {
                sharedList2.add(it)
            }
        }
    }
}
threads2.forEach { it.start() }
threads2.forEach { it.join() }
println(sharedList2)
  1. 内存泄漏解决方案
    • 避免长生命周期对象持有短生命周期对象的引用:在上述OuterClass示例中,如果thread运行结束后能及时释放对outer的引用,就可以避免内存泄漏。
    • 使用弱引用:在Android开发中,如果Activity中的Lambda表达式需要被后台线程持有,可以使用WeakReference来持有Activity的引用,这样当Activity销毁时,垃圾回收器可以回收Activity及其相关资源。
    • 示例(使用WeakReference
class OuterClass2 {
    val innerLambda: () -> Unit
    init {
        innerLambda = {
            println("This is a lambda in OuterClass2")
        }
    }
}
fun main() {
    val outer = OuterClass2()
    val weakOuter = WeakReference(outer)
    val thread2 = Thread {
        val outerRef = weakOuter.get()
        outerRef?.innerLambda?.invoke()
    }
    // 即使thread2运行过程中outer对象的生命周期结束,也不会造成内存泄漏
}