Correctly implementing wait and notify in Kotlin

后端 未结 3 1978
伪装坚强ぢ
伪装坚强ぢ 2021-01-01 11:54

According to this document, using wait and notify is discouraged in Kotlin: https://kotlinlang.org/docs/reference/java-interop.html

相关标签:
3条回答
  • 2021-01-01 12:26

    In general you should use higher-level concurrency utilities when possible.

    However, if none of the higher-level constructs work in your case, the direct replacement is to use a ReentrantLock and a single Condition on that lock.

    For example, if your Java code was something like:

    private Object lock = new Object();
    
    ...
    
    synchronized(lock) {
        ...
        lock.wait();
        ...
        lock.notify();
        ...
        lock.notifyAll();
        ...
    }
    

    You can change it to the following Kotlin:

    private val lock = ReentrantLock()
    private val condition = lock.newCondition()
    
    lock.withLock {           // like synchronized(lock)
        ...
        condition.await()     // like wait()
        ...
        condition.signal()    // like notify()
        ...
        condition.signalAll() // like notifyAll()
        ...
    }
    

    While this is slightly more verbose, conditions do provide some extra flexibility, as you can have multiple conditions on a single lock, and there are also other kinds of locks (notably ReentrantReadWriteLock.ReadLock and ReentrantReadWriteLock.WriteLock).

    Note that withLock is a Kotlin-provided extension function that takes care of calling Lock.lock()/Lock.unlock() before/after invoking the supplied lambda.

    0 讨论(0)
  • 2021-01-01 12:34

    A BlockingQueue can be a suitable high-level concurrency utility for your use case, but applying it requires knowing and modifying your code structure.

    The idea is, fetchData() should .take() an item from the queue, and if the queue is empty, that will block the execution until an item appears, which eliminates the .wait() in your code. The producer of the data should .put(t) the data into the queue.


    If you really need to use wait and notify, e.g. for implementing a concurrency utility at low-level, you can cast a Kotlin object to java.lang.Object and call these functions afterwards, as said in the language reference. Or, written as extension functions:

    @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
    private fun Any.wait() = (this as java.lang.Object).wait()
    
    0 讨论(0)
  • 2021-01-01 12:46

    You could use the kotlin Semaphore to suspend the coroutine instead of blocking a thread:

    val semaphore = Semaphore(1, 0)
    
    suspend fun runFetchLoop() = withContext(Dispatchers.Default){
        while (isActive) {
            val data = fetchData()
            processData(data)
            semaphore.acquire()
            if (data.isEmpty()) {
                semaphore.acquire()
            }
            semaphore.release()
        }
    }
    
    fun notifyDataAvailable() = semaphore.release()
    

    Here is a running example: https://pl.kotl.in/bnKeOspfs .

    However I would prefer a cold Flow or a hot Channel to solve this problem. Here is a good article about cold flows and hot channels of the great Roman Elizarov

    0 讨论(0)
提交回复
热议问题