假设我有以下线程安全类(注意没有suspend
函数):
@AnyThread
class MyClass {
@AnyThread
fun foo(): Int {
runWithoutConcurrency()
// .. the rest
}
@AnyThread
fun bar(): Int {
runWithoutConcurrency()
// .. the rest
}
@Synchronized
private fun runWithoutConcurrency() { /* .. */ }
}
假设有一个临界区(它本身不是线程安全的),所以它被放在runWithoutConcurrency()
并标记为@Synchronized
,这样整个类都是线程安全的。
现在让我们将foo()
改为suspend
函数,如何实现相同的线程安全性?
添加了更多详细信息:
如果API仅由suspend
函数组成,那么我将使用Kotlin协程Mutex
,因此这里的问题是如何将Java同步(无论如何都需要使bar()
线程安全)与Mutex
用法(无论如何都需要使suspend foo()
线程安全)“结合”起来。
1条答案
按热度按时间rqmkfv5c1#
Java中的同步块是通过阻塞线程来等待的,所以我们通常应该尽量避免从协程调用它们。如果没有其他选择,那么根据具体情况,我们可以使用
Mutex
,Dispatchers.IO
或它们的混合。1..如果synchronized方法只在一个地方被调用,或者我们控制所有这样的地方,并且所有这些地方都挂起-我们可以使用
Mutex
:我相信这应该是安全的,因为如果我们保证不并发调用
runWithoutConcurrency()
,那么它就不会阻塞,当然,我们必须在所有调用点使用完全相同的mutex
对象。2..正如@Tenfour04所建议的,如果我们同时从协程和非协程上下文中调用方法,但我们仍然控制所有调用点(如您的示例所示),我们可以选择仍然使用基于互斥锁的解决方案,对于非挂起上下文,使用
runBlocking()
启动协程。这里的想法是使用相同的同步机制同步这两种情况,并根据情况选择挂起或阻塞。来自
foo()
的线程永远不会阻塞,只会挂起。如果runWithoutConcurrency()
正在使用,来自bar()
的线程可能会阻塞,但这与使用synchronized方法本身是相同的。3..如果我们无法控制synchronized方法的所有调用点,或者无法轻松地将互斥锁传递到所有调用点,我们可以像处理其他阻塞代码一样处理这种情况--使用
Dispatchers.IO
:在这种情况下,
IO
可能听起来违反直觉,因为我们在这里不做任何I/O,但事实上,Dispatchers.IO
可以/应该用于任何类型的阻塞代码,而不仅仅是I/O。我仍然建议如果可能的话将其放在互斥锁中-这样我们最多阻塞一个IO线程,而不是每个调用一个线程。如果有多个
@Synchronized
方法,情况会稍有不同,因为它们都使用同一个对象进行同步,但一般规则仍然适用。