動態

詳情 返回 返回

Android面試題之Kotlin協程併發問題和互斥鎖 - 動態 詳情

本文首發於公眾號“AntDream”,歡迎微信搜索“AntDream”或掃描文章底部二維碼關注,和我一起每天進步一點點

Kotlin 語言提供了多種機制來處理併發和同步,其中包括高層次和低層次的工具。對於常規的併發任務,可以利用 Kotlin 協程提供的結構化併發方式。而對於需要更低層次的鎖定機制,可以使用 Mutex 來實現對共享資源的線程安全訪問。

Kotlin 協程與併發(Coroutines and Concurrency)

協程是一種輕量級的線程,可以通過 kotlinx.coroutines 庫來實現。協程為結構化併發提供了強大的支持,使得編寫異步、併發代碼變得更加簡單和直觀。

協程基礎

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(1000L)
        println("World!")
    }
    println("Hello,")
}

在這個例子中,runBlocking 函數用於啓動一個新的協程並阻塞當前線程,而 launch 函數則用於啓動一個新的協程,並在1秒後輸出 "World!"。

併發與同步

當多個協程需要訪問共享資源時,需要一些同步機制來防止數據競爭。一個常用的方法是使用 Kotlin 庫提供的 Mutex

Mutex

Mutex(互斥鎖)是一種用於保證互斥訪問共享資源的同步機制。Mutex 確保在同一時刻只有一個協程能夠訪問被保護的代碼塊或資源,從而避免競爭條件。

使用 Mutex

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

val mutex = Mutex()
var counter = 0

fun main() = runBlocking {
    val jobs = List(100) {
        launch {
            repeat(1000) {
                // 在這裏使用 mutex 來保護對 counter 的訪問
                mutex.withLock {
                    counter++
                }
            }
        }
    }
    jobs.forEach { it.join() }
    println("Counter = $counter")
}

在這個例子中,我們創建了100個協程,每個協程重複1000次對共享變量 counter 的訪問。使用 mutex.withLock 保證了每次只有一個協程能訪問 counter,從而避免併發問題。

withLock() 是一種便捷方法,用於在鎖內執行給定的代碼塊。它會自動處理獲取和釋放鎖,確保即使在代碼塊中發生異常,也會正確釋放鎖。

Mutex 的其他方法

lock:掛起直到互斥鎖被鎖定。

lock() 方法用於嘗試獲取鎖。如果鎖已經被其他協程持有,那麼調用 lock() 的協程將會被掛起,直到鎖變為可用。

用法
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex

val mutex = Mutex()

fun main() = runBlocking {
    launch {
        mutex.lock() // 獲取鎖
        try {
            // 保護的代碼段
            println("Locked by coroutine 1")
            delay(1000)
        } finally {
            mutex.unlock() // 確保釋放鎖
        }
    }

    launch {
        mutex.lock() // 等待並獲取鎖
        try {
            // 保護的代碼段
            println("Locked by coroutine 2")
        } finally {
            mutex.unlock() // 確保釋放鎖
        }
    }
}

unlock:解鎖互斥鎖。

unlock() 方法用於釋放鎖,使得被掛起的其他協程可以繼續執行。如果 unlock() 被調用時沒有持有鎖,則會引發異常。

用法

如上面 lock() 示例中的 finally 塊所示。

tryLock

tryLock() 嘗試獲取鎖,如果鎖當前是可用的,則立即獲取鎖並返回 true;否則返回 false,且不會掛起當前協程。

用法
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex

val mutex = Mutex()

fun main() = runBlocking {
    launch {
        if (mutex.tryLock()) { // 嘗試獲取鎖
            try {
                println("Lock acquired by coroutine 1")
                delay(1000)
            } finally {
                mutex.unlock()
            }
        } else {
            println("Coroutine 1: Lock not acquired")
        }
    }

    launch {
        if (mutex.tryLock()) { // 嘗試獲取鎖
            try {
                println("Lock acquired by coroutine 2")
            } finally {
                mutex.unlock()
            }
        } else {
            println("Coroutine 2: Lock not acquired")
        }
    }
}

總結

  • lock():嘗試獲取鎖,如果鎖不可用,則掛起當前協程。
  • unlock():釋放鎖,其他掛起的協程可以繼續執行。
  • tryLock():嘗試獲取鎖,如果鎖不可用,則立即返回 false,不會掛起當前協程。
  • withLock():便捷方法,自動獲取和釋放鎖,確保在代碼塊執行後釋放鎖。

Mutex 的這些方法使得在 Kotlin 協程中進行線程安全的操作變得更加簡潔和直觀。根據實際需求選擇合適的方法,可以有效避免併發問題,提高代碼的健壯性和可維護性。


歡迎關注我的公眾號AntDream查看更多精彩文章!

user avatar huangxingyu 頭像 u_15702012 頭像 maenj_ba_lah 頭像 mannayang 頭像 shenchendexiaoyanyao 頭像 wxweven 頭像 snower 頭像 koogua 頭像 wuxiedekeben 頭像 caisekongbai 頭像 jieduanxingdebugger 頭像 hunter_58d48c41761b8 頭像
點贊 19 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.