Kotlin协程多线程调度器下局部变量线程安全与可见性问题咨询
关于Kotlin协程跨线程调度的变量可见性问题解答
结论
这段代码是线程安全的,变量可见性由Kotlin协程调度机制和Java内存模型(JMM)的先行发生(happen-before)规则共同保证。
原理说明
- 单协程的串行执行特性
你通过launch启动的是单个协程,无论底层线程池有多少可用线程,同一时间该协程的代码只会在一个线程上执行。yield()仅会主动让出当前占用的线程,让调度器将当前协程挂起,后续再调度到任意空闲线程上恢复执行,全程不会出现同一段协程逻辑并行执行的情况,天然不存在竞态条件。 - Executor的happen-before规则保障可见性
你使用的Executors.newFixedThreadPool()属于JDK的ThreadPoolExecutor实现,JMM明确规定:提交到Executor的任务执行前,提交任务动作之前的所有操作都对该任务可见。
协程挂起后恢复的本质,就是调度器把协程的续体(Continuation,即协程暂停后要继续执行的代码块)作为新任务提交给线程池。前一次协程挂起前对变量x的修改,一定先行发生于后续续体在新线程上的执行操作,因此新线程必然能读取到最新的x值,不存在可见性问题。 - 协程状态机实现兜底
Kotlin协程会将挂起代码块编译为状态机结构,你代码中的局部变量x、threads都会被提升为对应续体类的成员变量,每次恢复执行时直接操作该成员变量的值,配合前述happen-before规则,完全不会出现跨线程更新不可见的问题。
补充
只有当多个协程同时修改同一个共享外部变量时,才需要额外通过锁、原子类等方式保证线程安全。单个协程内部的逻辑,即使跨多线程调度,也默认是线程安全的,不需要额外处理。
内容的提问来源于stack exchange,提问作者Val
相关产品推荐
相关产品推荐

