kotlinx.coroutines 是由 JetBrains 开发的功能丰富的协程库。它包含本指南中涵盖的很多启用高级协程的原语,包括 launch、 async 等等。
本文是通过反编译协程相关class文件,分析协程的实现原理。
第一个基本协程代码
import kotlinx.coroutines.* fun main() { println("Starting...") GlobalScope.launch { repeat(10) { i -> println("I'm sleeping $i ...") delay(500L) } } println("Hello,") Thread.sleep(20000L) }输出结果
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和T1kt$main$1.class
自动创建了public static void main(java.lang.String[]) main方法,通过invokestatic字节码调用public static final void main方法。
此方法对应于T1.kt中的fun main方法
0:从常量池加载字面量"Starting"9:打印"Starting"20:创建T1Kt$main$1类25:初始化T1Kt$main$1对象33:调用静态方法kotlinx/coroutines/BuildersKt.launch,入参是GlobalScope、null、null、T1Kt$main$1对象(Function2接口的实例)46:打印Hello52:休眠20000毫秒,等待协程执行完BuildersKt.launch方法的入参为CoroutineScope、CoroutineContext、CoroutineStart、Function2、Object。在此场景下:
CoroutineScope:GlobalScope,默认的顶层协程CoroutineContext:四种协程上下文Unconfined、Default、newSingleThreadContext、runBlocking,此时使用Default调度器,使用一个JVM内的共享线程池CoroutineStart:DEFAULT,立即调度协程并使用指定的CoroutineContextT1kt$main$1继承了抽象类kotlin.coroutines.jvm.internal.SuspendLambda,并实现了接口kotlin.jvm.functions.Function2
协程执行入口
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本方法为协程的具体执行逻辑。
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字段对应于T1Kt$main$1.label。 label是个int类型的标识,用于管理Coroutine的状态机9:由于分支集中,仅两个此处使用了tableswitch字节码,而非lookupswitch141:修改label为1,默认值0144:调用delay方法,挂起协程500毫秒150:回边判断179:goto回循环开始