一、背景
kotlinx.coroutines 是由 JetBrains 开发的功能丰富的协程库。它包含本指南中涵盖的很多启用高级协程的原语,包括 launch、 async 等等。
本文是通过反编译协程相关class文件,分析协程的实现原理。
二、 Kotlin代码 T1.kt
第一个基本协程代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| import kotlinx.coroutines.*
fun main() { println("Starting...") GlobalScope.launch { repeat(10) { i -> println("I'm sleeping $i ...") delay(500L) } } println("Hello,") Thread.sleep(20000L) }
|
输出结果
1 2 3 4 5 6 7 8 9 10 11 12
| Starting... Hello, I'm sleeping 0 ... I'm sleeping 1 ... I'm sleeping 2 ... I'm sleeping 3 ... I'm sleeping 4 ... I'm sleeping 5 ... I'm sleeping 6 ... I'm sleeping 7 ... I'm sleeping 8 ... I'm sleeping 9 ...
|
三、字节码
T1.kt编译后出现两个class文件T1kt.class和T1ktmain1.class
3.1 T1kt.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public final class T1Kt { public static final void main(); Code: 0: ldc #11 // String Starting... 2: astore_0 3: iconst_0 4: istore_1 5: getstatic #17 // Field java/lang/System.out:Ljava/io/PrintStream; 8: aload_0 9: invokevirtual #23 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 12: getstatic #29 // Field kotlinx/coroutines/GlobalScope.INSTANCE:Lkotlinx/coroutines/GlobalScope; 15: checkcast #31 // class kotlinx/coroutines/CoroutineScope 18: aconst_null 19: aconst_null 20: new #33 // class T1Kt$main$1 23: dup 24: aconst_null 25: invokespecial #37 // Method T1Kt$main$1."<init>":(Lkotlin/coroutines/Continuation;)V 28: checkcast #39 // class kotlin/jvm/functions/Function2 31: iconst_3 32: aconst_null 33: invokestatic #45 // Method kotlinx/coroutines/BuildersKt.launch$default:(Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Job; 36: pop 37: ldc #47 // String Hello, 39: astore_0 40: iconst_0 41: istore_1 42: getstatic #17 // Field java/lang/System.out:Ljava/io/PrintStream; 45: aload_0 46: invokevirtual #23 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 49: ldc2_w #48 // long 20000l 52: invokestatic #55 // Method java/lang/Thread.sleep:(J)V 55: return
public static void main(java.lang.String[]); Code: 0: invokestatic #9 // Method main:()V 3: return }
|
3.1.1 public static void main(java.lang.String[]) main
自动创建了public static void main(java.lang.String[]) main方法,通过invokestatic字节码调用public static final void main方法。
3.1.2 public static final void main
此方法对应于T1.kt中的fun main方法
* 0:从常量池加载字面量”Starting”
* 9:打印”Starting”
* 20:创建T1Ktmain1类
* 25:初始化T1Ktmain1对象
* 33:调用静态方法kotlinx/coroutines/BuildersKt.launch,入参是GlobalScope、null、null、T1Ktmain1对象(Function2接口的实例)
* 46:打印Hello
* 52:休眠20000毫秒,等待协程执行完
BuildersKt.launch方法的入参为CoroutineScope、CoroutineContext、CoroutineStart、Function2、Object。在此场景下:
* CoroutineScope:GlobalScope,默认的顶层协程
* CoroutineContext:四种协程上下文Unconfined、Default、newSingleThreadContext、runBlocking,此时使用Default调度器,使用一个JVM内的共享线程池
* CoroutineStart:DEFAULT,立即调度协程并使用指定的CoroutineContext
3.2 T1ktmain1.class
T1ktmain1继承了抽象类kotlin.coroutines.jvm.internal.SuspendLambda,并实现了接口kotlin.jvm.functions.Function2
3.2.1 public final java.lang.Object invoke(java.lang.Object, java.lang.Object)
协程执行入口
1 2 3 4 5 6 7 8 9
| 0: aload_0 1: aload_1 2: aload_2 3: checkcast #149 // class kotlin/coroutines/Continuation 6: invokevirtual #151 // Method create:(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation; 9: checkcast #2 // class T1Kt$main$1 12: getstatic #109 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit; 15: invokevirtual #153 // Method invokeSuspend:(Ljava/lang/Object;)Ljava/lang/Object; 18: areturn
|
- 0-2:加载this和两个入参(CoroutineScope和Coroutine)
- 15:调用实例方法invokeSuspend
3.2.2 public final java.lang.Object invokeSuspend
本方法为协程的具体执行逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| 0: invokestatic #34 // Method kotlin/coroutines/intrinsics/IntrinsicsKt.getCOROUTINE_SUSPENDED:()Ljava/lang/Object; 3: astore 11 5: aload_0 6: getfield #37 // Field label:I 9: tableswitch { // 0 to 1 0: 32 1: 156 default: 190 } 32: aload_1 33: invokestatic #43 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V 36: aload_0 37: getfield #45 // Field p$:Lkotlinx/coroutines/CoroutineScope; 40: astore_2 41: bipush 10 43: istore_3 44: iconst_0 45: istore 4 47: iconst_0 48: istore 5 50: iconst_0 51: istore 5 53: iload_3 54: istore 6 56: iload 5 58: iload 6 60: if_icmpge 186 63: iload 5 65: invokestatic #51 // Method kotlin/coroutines/jvm/internal/Boxing.boxInt:(I)Ljava/lang/Integer; 68: checkcast #53 // class java/lang/Number 71: invokevirtual #57 // Method java/lang/Number.intValue:()I 74: istore 7 76: iconst_0 77: istore 8 79: new #59 // class java/lang/StringBuilder 82: dup 83: invokespecial #63 // Method java/lang/StringBuilder."<init>":()V 86: ldc #65 // String I'm sleeping 88: invokevirtual #69 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 91: iload 7 93: invokevirtual #72 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 96: ldc #74 // String ... 98: invokevirtual #69 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 101: invokevirtual #78 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 104: astore 9 106: iconst_0 107: istore 10 109: getstatic #84 // Field java/lang/System.out:Ljava/io/PrintStream; 112: aload 9 114: invokevirtual #89 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 117: ldc2_w #90 // long 500l 120: aload_0 121: aload_0 122: iload 5 124: putfield #93 // Field I$0:I 127: aload_0 128: iload 6 130: putfield #95 // Field I$1:I 133: aload_0 134: iload 7 136: putfield #97 // Field I$2:I 139: aload_0 140: iconst_1 141: putfield #37 // Field label:I 144: invokestatic #103 // Method kotlinx/coroutines/DelayKt.delay:(JLkotlin/coroutines/Continuation;)Ljava/lang/Object; 147: dup 148: aload 11 150: if_acmpne 179 153: aload 11 155: areturn 156: aload_0 157: getfield #97 // Field I$2:I 160: istore 7 162: aload_0 163: getfield #95 // Field I$1:I 166: istore 6 168: aload_0 169: getfield #93 // Field I$0:I 172: istore 5 174: aload_1 175: invokestatic #43 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V 178: aload_1 179: pop 180: iinc 5, 1 183: goto 56 186: getstatic #109 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit; 189: areturn 190: new #111 // class java/lang/IllegalStateException 193: dup 194: ldc #113 // String call to 'resume' before 'invoke' with coroutine 196: invokespecial #116 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V 199: athrow
|
- 6: 获取对象37字段的值,并压入栈,37字段对应于T1Ktmain1.label。
label是个int类型的标识,用于管理Coroutine的状态机
- 9:由于分支集中,仅两个此处使用了tableswitch字节码,而非lookupswitch
- 141:修改label为1,默认值0
- 144:调用delay方法,挂起协程500毫秒
- 150:回边判断
- 179:goto回循环开始
四、总结
- Kotlin协程通过编译器织入字节码实现
- 协程执行逻辑被放入一个内部匿名类,继承了抽象类kotlin.coroutines.jvm.internal.SuspendLambda,并实现了接口kotlin.jvm.functions.Function2
- 调度器调度入口是Function2的invoke方法
- 通过delay方法挂起协程,该方法不会阻塞线程