Understanding Kotlin Coroutines, Go Coroutines, and JVM Threading
The article demonstrates that Kotlin coroutines on the JVM are merely a convenient API layered on traditional Java threads rather than true lightweight coroutines, contrasting them with Go’s M:N goroutine scheduling, and discusses experimental Kotlin‑Native and OpenJDK Loom projects that aim to provide genuine coroutine support.
Most online articles claim that Kotlin coroutines are highly efficient and outperform threads, but this article investigates whether that claim holds true.
Coroutines are not a new concept; a basic coroutine model can be implemented in C++ with less than 50 lines of code. The article adopts the definition of coroutines mainly from Go, as the concept has become popular after Go's rise.
1. Mainstream definition of Kotlin coroutines on the Internet
The discussion originates from a WeChat article titled "After Go, is Java still the best choice?" The author wonders whether Kotlin truly implements a coroutine mechanism similar to Go's, or if the numerous blog posts are misleading.
2. Relationship between JVM Thread and OS Thread
In most JVM implementations (Oracle JVM, Android ART), a Java Thread maps 1:1 to an OS thread. Each Thread consumes about 1 MB of memory, and context switches introduce overhead. The article shows the native JNI call chain that creates a thread, with screenshots of the source files ( Thread.c, jvm.cpp, and Linux-specific os_linux.cpp).
3. What Go does with coroutines
Go schedules many goroutines onto a small number of OS threads (M:N model). Each goroutine uses only about 4 KB of stack, far less than a Java thread. The article provides a Go example that launches ten goroutines, each printing its thread ID, demonstrating that many concurrent tasks can run on a single thread.
package main
import (
"fmt"
"runtime"
"strconv"
"time"
"golang.org/x/sys/windows"
)
func name(s string) {
for {
// sleep 1s for readability
time.Sleep(time.Second)
str := fmt.Sprint(windows.GetCurrentThreadId())
var s = "iqoo" + s + " belong thread " + str
fmt.Println(s)
}
}
func main() {
fmt.Println("逻辑cpu数量:" + strconv.Itoa(runtime.NumCPU()))
str := fmt.Sprint(windows.GetCurrentThreadId())
fmt.Println("主协程所属线程id =" + str)
for i := 1; i <= 10; i++ {
go name(strconv.Itoa(i))
}
time.Sleep(100 * time.Second)
}4. Java vs Go concurrency
The article shows a minimal Java program that starts two threads, each printing a message in an infinite loop, illustrating that Java relies on OS threads for concurrency.
package com.wuyue;
public class JavaCode {
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
while (true) {
System.out.println("iqoo " + Thread.currentThread().getName());
try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
}
}
}.start();
new Thread() {
@Override
public void run() {
while (true) {
System.out.println("x27 " + Thread.currentThread().getName());
try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
}
}
}.start();
}
}In Go, the same logical tasks are handled by many goroutines on far fewer OS threads, confirming the M:N scheduling advantage.
5. Does Kotlin-JVM provide true coroutines?
The article argues that Kotlin-JVM cannot achieve the same coroutine capabilities as Go. Kotlin’s coroutine support on the JVM is essentially a wrapper around Java threads, relying on synchronized and other Java synchronization primitives.
6. Locks in Kotlin
Kotlin does not introduce its own lock keyword; it uses Java’s synchronization mechanisms. The article shows a Kotlin class using the @Synchronized annotation and decompiles it to reveal the underlying Java bytecode.
class PrintTest {
@Synchronized fun print() {
println("hello world")
}
@Synchronized fun print2() {
println("hello world")
}
}7. Future of coroutines in Kotlin
Kotlin‑Native, which compiles Kotlin to native binaries, offers a worker model that can provide true coroutine-like concurrency, though it is still experimental and only supports Linux and macOS.
8. JVM plans for coroutines
Projects like Quasar (bytecode injection) and OpenJDK’s Loom aim to bring lightweight coroutine support to the JVM.
9. What Kotlin coroutines actually are
On the JVM, Kotlin coroutines are essentially a more convenient API over Java threads, using the suspend keyword to mark functions that must be called from a coroutine context. They simplify asynchronous code but do not eliminate thread usage.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch(Dispatchers.Main) {
getInfo()
getInfoNoContext()
Log.v("wuyue", "我又切回来了 in thread " + Thread.currentThread().name)
}
}
suspend fun getInfo() {
withContext(Dispatchers.IO) {
Log.v("wuyue", "getInfo in thread " + Thread.currentThread().name)
}
}
suspend fun getInfoNoContext() {
Log.v("wuyue", "getInfoNoContext in thread " + Thread.currentThread().name)
}
}10. Summary
Kotlin-JVM coroutines are essentially wrappers around Java threads, not true lightweight coroutines.
Their "suspend" behavior merely offloads work to another thread without blocking the original one.
Kotlin‑Native may eventually provide genuine coroutine support.
For Java developers, the number of OS threads equals the number of JVM threads unless a custom runtime (e.g., Loom) is used.
OpenJDK Loom is the most promising effort to bring true coroutine semantics to the JVM.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
