MST

星途 面试题库

面试题:Kotlin多线程同步中如何使用Mutex

在Kotlin中,描述如何使用Mutex(互斥锁)来实现多线程同步。请举例说明Mutex如何防止多个线程同时访问共享资源,避免数据竞争问题。同时,阐述Mutex在加锁和解锁过程中的注意事项。
45.8万 热度难度
编程语言Kotlin

知识考点

AI 面试

面试题答案

一键面试

使用Mutex实现多线程同步及避免数据竞争

在Kotlin中,可以使用 kotlinx.coroutines.sync.Mutex 来实现多线程同步。以下是一个简单的示例:

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex

// 创建一个Mutex实例
val mutex = Mutex()
// 共享资源
var sharedResource = 0

fun main() = runBlocking {
    // 创建多个协程来模拟多线程访问
    val jobs = List(10) {
        launch {
            mutex.lock()
            try {
                // 访问共享资源
                sharedResource++
                println("Thread ${Thread.currentThread().name} incremented sharedResource to $sharedResource")
            } finally {
                mutex.unlock()
            }
        }
    }
    jobs.forEach { it.join() }
}

在上述代码中:

  1. 首先创建了一个 Mutex 实例 mutex
  2. 定义了共享资源 sharedResource
  3. 在协程中,通过 mutex.lock() 来获取锁,只有获取到锁的协程才能进入临界区访问共享资源。
  4. 使用 try - finally 块确保在访问完共享资源后,无论是否发生异常,都能通过 mutex.unlock() 释放锁,从而让其他协程有机会获取锁并访问共享资源。这样就防止了多个线程同时访问共享资源,避免了数据竞争问题。

Mutex加锁和解锁过程中的注意事项

  1. 锁的获取与释放匹配:必须确保每次调用 lock() 都有对应的 unlock() 调用。使用 try - finally 块可以有效保证这一点,即使在临界区内发生异常,锁也能被正确释放。如果没有正确释放锁,会导致死锁,其他等待该锁的线程将永远无法获取锁。
  2. 避免锁的滥用:虽然Mutex能解决数据竞争问题,但过多地使用锁会降低程序的并发性能。因为每次只有一个线程能获取锁进入临界区,所以应尽量缩小临界区的范围,只在真正需要保护共享资源的代码段加锁。
  3. 锁的性能开销:获取和释放锁本身也有一定的性能开销,尤其是在高并发场景下。因此,要权衡使用Mutex带来的同步安全性和性能损失之间的关系。
  4. 死锁风险:除了未正确释放锁导致死锁外,如果多个线程以不同顺序获取多个锁,也可能导致死锁。例如,线程A获取锁1后尝试获取锁2,而线程B获取锁2后尝试获取锁1,此时就可能发生死锁。需要通过合理设计锁的获取顺序等方式来避免这种情况。