├── .gitignore ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src ├── main └── kotlin │ ├── section1 │ ├── code1 │ │ └── Code1-1.kt │ ├── code2 │ │ └── Code1-2.kt │ ├── code3 │ │ └── Code1-3.kt │ ├── code4 │ │ └── code1_4.kt │ ├── code5 │ │ └── code1_5.kt │ └── code6 │ │ └── Code1-6.kt │ ├── section10 │ ├── code1 │ │ └── Code10-1.kt │ ├── code2 │ │ └── Code10-2.kt │ ├── code3 │ │ └── Code10-3.kt │ ├── code4 │ │ └── Code10-4.kt │ ├── code5 │ │ └── Code10-5.kt │ ├── code6 │ │ └── Code10-6.kt │ ├── code7 │ │ └── Code10-7.kt │ └── code8 │ │ └── Code10-8.kt │ ├── section11 │ ├── code1 │ │ └── Code11-1.kt │ ├── code10 │ │ └── Code11-10.kt │ ├── code11 │ │ └── Code11-11.kt │ ├── code12 │ │ └── Code11-12.kt │ ├── code13 │ │ └── Code11-13.kt │ ├── code14 │ │ └── Code11-14.kt │ ├── code15 │ │ └── Code11-15.kt │ ├── code16 │ │ └── Code11-16.kt │ ├── code17 │ │ └── Code11-17.kt │ ├── code18 │ │ └── Code11-18.kt │ ├── code19 │ │ └── Code11-19.kt │ ├── code2 │ │ └── Code11-2.kt │ ├── code20 │ │ └── Code11-20.kt │ ├── code21 │ │ └── Code11-21.kt │ ├── code22 │ │ └── Code11-22.kt │ ├── code3 │ │ └── Code11-3.kt │ ├── code4 │ │ └── Code11-4.kt │ ├── code5 │ │ └── Code11-5.kt │ ├── code6 │ │ └── Code11-6.kt │ ├── code7 │ │ └── Code11-7.kt │ ├── code8 │ │ └── Code11-8.kt │ └── code9 │ │ └── Code11-9.kt │ ├── section12 │ ├── code1 │ │ └── AddUseCase.kt │ ├── code2 │ │ ├── UserNameRepository.kt │ │ ├── UserPhoneNumberRepository.kt │ │ ├── UserProfile.kt │ │ └── UserProfileFetcher.kt │ ├── code3 │ │ └── RepeatAddUseCase.kt │ ├── code4 │ │ └── RepeatAddWithDelayUseCase.kt │ ├── code6 │ │ ├── Follower.kt │ │ ├── FollowerSearcher.kt │ │ ├── OfficialAccountRepository.kt │ │ └── PersonAccountRepository.kt │ └── code7 │ │ └── StringStateHolder.kt │ ├── section2 │ ├── code1 │ │ └── Code2-1.kt │ └── code2 │ │ └── Code2-2.kt │ ├── section3 │ ├── code1 │ │ └── Code3-1.kt │ ├── code2 │ │ └── Code3-2.kt │ ├── code3 │ │ └── Code3-3.kt │ ├── code4 │ │ └── Code3-4.kt │ ├── code5 │ │ └── Code3-5.kt │ ├── code6 │ │ └── Code3-6.kt │ ├── code7 │ │ └── Code3-7.kt │ └── code8 │ │ └── Code3-8.kt │ ├── section4 │ ├── code1 │ │ └── Code4-1.kt │ ├── code10 │ │ └── Code4-10.kt │ ├── code11 │ │ └── Code4-11.kt │ ├── code12 │ │ └── Code4-12.kt │ ├── code13 │ │ └── Code4-13.kt │ ├── code14 │ │ └── Code4-14.kt │ ├── code15 │ │ └── Code4-15.kt │ ├── code16 │ │ └── Code4-16.kt │ ├── code17 │ │ └── Code4-17.kt │ ├── code18 │ │ └── Code4-18.kt │ ├── code19 │ │ └── Code4-19.kt │ ├── code2 │ │ └── Code4-2.kt │ ├── code20 │ │ └── Code4-20.kt │ ├── code21 │ │ └── Code4-21.kt │ ├── code22 │ │ └── Code4-22.kt │ ├── code23 │ │ └── Code4-23.kt │ ├── code24 │ │ └── Code4-24.kt │ ├── code25 │ │ └── Code4-25.kt │ ├── code26 │ │ └── Code4-26.kt │ ├── code27 │ │ └── Code4-27.kt │ ├── code3 │ │ └── Code4-3.kt │ ├── code4 │ │ └── Code4-4.kt │ ├── code5 │ │ └── Code4-5.kt │ ├── code6 │ │ └── Code4-6.kt │ ├── code7 │ │ └── Code4-7.kt │ ├── code8 │ │ └── Code4-8.kt │ └── code9 │ │ └── Code4-9.kt │ ├── section5 │ ├── code1 │ │ └── Code5-1.kt │ ├── code10 │ │ └── Code5-10.kt │ ├── code11 │ │ └── Code5-11.kt │ ├── code12 │ │ └── Code5-12.kt │ ├── code13 │ │ └── Code5-13.kt │ ├── code14 │ │ └── Code5-14.kt │ ├── code15 │ │ └── Code5-15.kt │ ├── code2 │ │ └── Code5-2.kt │ ├── code3 │ │ └── Code5-3.kt │ ├── code4 │ │ └── Code5-4.kt │ ├── code5 │ │ └── Code5-5.kt │ ├── code6 │ │ └── Code5-6.kt │ ├── code7 │ │ └── Code5-7.kt │ ├── code8 │ │ └── Code5-8.kt │ └── code9 │ │ └── Code5-9.kt │ ├── section6 │ ├── code1 │ │ └── Code6-1.kt │ ├── code10 │ │ └── Code6-10.kt │ ├── code2 │ │ └── Code6-2.kt │ ├── code3 │ │ └── Code6-3.kt │ ├── code4 │ │ └── Code6-4.kt │ ├── code5 │ │ └── Code6-5.kt │ ├── code6 │ │ └── Code6-6.kt │ ├── code7 │ │ └── Code6-7.kt │ ├── code8 │ │ └── Code6-8.kt │ └── code9 │ │ └── Code6-9.kt │ ├── section7 │ ├── code1 │ │ └── Code7-1.kt │ ├── code10 │ │ └── Code7-10.kt │ ├── code11 │ │ └── Code7-11.kt │ ├── code12 │ │ └── Code7-12.kt │ ├── code13 │ │ └── Code7-13.kt │ ├── code14 │ │ └── Code7-14.kt │ ├── code15 │ │ └── Code7-15.kt │ ├── code16 │ │ └── Code7-16.kt │ ├── code17 │ │ └── Code7-17.kt │ ├── code18 │ │ └── Code7-18.kt │ ├── code19 │ │ └── Code7-19.kt │ ├── code2 │ │ └── Code7-2.kt │ ├── code20 │ │ └── Code7-20.kt │ ├── code21 │ │ └── Code7-21.kt │ ├── code22 │ │ └── Code7-22.kt │ ├── code23 │ │ └── Code7-23.kt │ ├── code24 │ │ └── Code7-24.kt │ ├── code25 │ │ └── Code7-25.kt │ ├── code26 │ │ └── Code7-26.kt │ ├── code27 │ │ └── Code7-27.kt │ ├── code28 │ │ └── Code7-28.kt │ ├── code29 │ │ └── Code7-29.kt │ ├── code3 │ │ └── Code7-3.kt │ ├── code30 │ │ └── Code7-30.kt │ ├── code31 │ │ └── Code7-31.kt │ ├── code32 │ │ └── Code7-32.kt │ ├── code4 │ │ └── Code7-4.kt │ ├── code5 │ │ └── Code7-5.kt │ ├── code6 │ │ └── Code7-6.kt │ ├── code7 │ │ └── Code7-7.kt │ ├── code8 │ │ └── Code7-8.kt │ └── code9 │ │ └── Code7-9.kt │ ├── section8 │ ├── code1 │ │ └── Code8-1.kt │ ├── code10 │ │ └── Code8-10.kt │ ├── code11 │ │ └── Code8-11.kt │ ├── code12 │ │ └── Code8-12.kt │ ├── code13 │ │ └── Code8-13.kt │ ├── code14 │ │ └── Code8-14.kt │ ├── code15 │ │ └── Code8-15.kt │ ├── code16 │ │ └── Code8-16.kt │ ├── code17 │ │ └── Code8-17.kt │ ├── code18 │ │ └── Code8-18.kt │ ├── code19 │ │ └── Code8-19.kt │ ├── code2 │ │ └── Code8-2.kt │ ├── code3 │ │ └── Code8-3.kt │ ├── code4 │ │ └── Code8-4.kt │ ├── code5 │ │ └── Code8-5.kt │ ├── code6 │ │ └── Code8-6.kt │ ├── code7 │ │ └── Code8-7.kt │ ├── code8 │ │ └── Code8-8.kt │ └── code9 │ │ └── Code8-9.kt │ └── section9 │ ├── code1 │ └── Code9-1.kt │ ├── code10 │ └── Code9-10.kt │ ├── code11 │ └── Code9-11.kt │ ├── code12 │ └── Code9-12.kt │ ├── code2 │ └── Code9-2.kt │ ├── code3 │ └── Code9-3.kt │ ├── code4 │ └── Code9-4.kt │ ├── code5 │ └── Code9-5.kt │ ├── code6 │ └── Code9-6.kt │ ├── code7 │ └── Code9-7.kt │ ├── code8 │ └── Code9-8.kt │ └── code9 │ └── Code9-9.kt └── test └── kotlin └── chapter12 ├── code1 ├── AddUseCaseTest.kt └── AddUseCaseTestWithBeforeEach.kt ├── code2 ├── FakeUserPhoneNumberRepository.kt ├── StubUserNameRepository.kt └── UserProfileFetcherTest.kt ├── code3 └── RepeatAddUseCaseTest.kt ├── code4 └── RepeatAddWithDelayUseCaseTest.kt ├── code5 └── TestCoroutineScheduler.kt ├── code6 ├── FollowerSearcherTest.kt ├── StubOfficialAccountRepository.kt └── StubPersonAccountRepository.kt ├── code7 ├── StringStateHolderTestFail.kt └── StringStateHolderTestSuccess.kt └── code8 └── BackgroundScopeTest.kt /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store 43 | /.idea/ 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 표지 3 |

4 | 5 |
6 | 7 | ## 소개 8 | 『코틀린 코루틴 완전 정복』, 인프런(2024) 강의의 코드를 모아 놓은 저장소 입니다. 9 | 10 |
11 | 12 | ## 코드 실행 방법 13 | ### 예시 코드 14 | 예시 코드는 main 함수 옆의 재생 버튼(►)을 누르고, Run '파일명' 버튼을 누르면 실행 가능합니다.
15 | 스크린샷 2024-02-13 오후 4 16 29 16 | 17 |
18 | 19 | ## 링크 목록 20 | - 카카오톡 오픈챗: [코틀린, 안드로이드, 스프링 사용자 모임](https://open.kakao.com/o/gAmC7aVd) 21 | 22 |
23 | 24 | ## 코드 문의 및 오타 신고 ⚠️ 25 | - 강사 이메일: seyoungcho2@gmail.com 26 | 27 |
28 | 29 | ## 강의가 유용하다고 생각하셨나요?👍 30 | - 깃허브 스타⭐를 통해 강의를 지지해주세요!
강의을 지지한 사람을 [Stargazers](https://github.com/seyoungcho2/coroutinelecture/stargazers) 페이지에서 볼 수 있습니다. 31 | - 강의가 어땠는지 수강평을 남겨주세요! 32 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "2.0.0" 3 | } 4 | 5 | group = "info.coroutine" 6 | version = "1.0-SNAPSHOT" 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | // 코루틴 라이브러리 14 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") 15 | 16 | // JUnit5 테스트 프레임워크 17 | testImplementation("org.junit.jupiter:junit-jupiter-api:5.11.1") 18 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.11.1") 19 | 20 | // 코루틴 테스트 라이브러리 21 | testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1") 22 | } 23 | 24 | tasks.test { 25 | useJUnitPlatform() 26 | } 27 | 28 | kotlin { 29 | jvmToolchain(17) 30 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seyoungcho2/coroutinelecture/8ffd548762cd2d3195085116a4707cfad3a41b03/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jun 25 04:03:57 KST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 3 | } 4 | rootProject.name = "coroutinelecture" 5 | 6 | -------------------------------------------------------------------------------- /src/main/kotlin/section1/code1/Code1-1.kt: -------------------------------------------------------------------------------- 1 | package section1.code1 2 | 3 | fun main() { 4 | println("[${Thread.currentThread().name}] 시작") 5 | Thread.sleep(1000L) 6 | println("[${Thread.currentThread().name}] 종료") 7 | } -------------------------------------------------------------------------------- /src/main/kotlin/section1/code2/Code1-2.kt: -------------------------------------------------------------------------------- 1 | package section1.code2 2 | 3 | class ExampleThread : Thread() { 4 | override fun run() { 5 | println("[${Thread.currentThread().name}] 시작") 6 | Thread.sleep(2000L) 7 | println("[${Thread.currentThread().name}] 종료") 8 | } 9 | } 10 | 11 | fun main() { 12 | println("[${Thread.currentThread().name}] 시작") 13 | ExampleThread().start() 14 | Thread.sleep(1000L) 15 | println("[${Thread.currentThread().name}] 종료") 16 | } 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/kotlin/section1/code3/Code1-3.kt: -------------------------------------------------------------------------------- 1 | package section1.code3 2 | 3 | import kotlin.concurrent.thread 4 | 5 | fun main() { 6 | println("[${Thread.currentThread().name}] 시작") 7 | thread { 8 | println("[${Thread.currentThread().name}] 시작") 9 | Thread.sleep(2000L) 10 | println("[${Thread.currentThread().name}] 종료") 11 | } 12 | Thread.sleep(1000L) 13 | println("[${Thread.currentThread().name}] 종료") 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/section1/code4/code1_4.kt: -------------------------------------------------------------------------------- 1 | package section1.code4 2 | 3 | import java.util.concurrent.Executors 4 | 5 | fun main() { 6 | // ExecutorService 생성 7 | val executorService = Executors.newFixedThreadPool(2) 8 | 9 | // 작업1 제출 10 | executorService.submit { 11 | println("[${Thread.currentThread().name}] 작업1 시작") 12 | Thread.sleep(1000L) // 1초간 대기 13 | println("[${Thread.currentThread().name}] 작업1 완료") 14 | } 15 | 16 | // 작업2 제출 17 | executorService.submit { 18 | println("[${Thread.currentThread().name}] 작업2 시작") 19 | Thread.sleep(1000L) // 1초간 대기 20 | println("[${Thread.currentThread().name}] 작업2 완료") 21 | } 22 | 23 | // 작업3 제출 24 | executorService.submit { 25 | println("[${Thread.currentThread().name}] 작업3 시작") 26 | Thread.sleep(1000L) // 1초간 대기 27 | println("[${Thread.currentThread().name}] 작업3 완료") 28 | } 29 | 30 | // ExecutorService 종료 31 | executorService.shutdown() 32 | } 33 | -------------------------------------------------------------------------------- /src/main/kotlin/section1/code5/code1_5.kt: -------------------------------------------------------------------------------- 1 | package section1.code5 2 | 3 | import java.util.concurrent.Executors 4 | import java.util.concurrent.Future 5 | 6 | fun main() { 7 | val executorService = Executors.newFixedThreadPool(2) 8 | 9 | // ExecutorService에 반환 값이 있는 작업 제출 10 | val future: Future = executorService.submit { 11 | Thread.sleep(2000) 12 | return@submit "더미 결과값" 13 | } 14 | 15 | // 반환값이 올때까지 메인 스레드 블로킹 16 | val result = future.get() 17 | println(result) 18 | 19 | executorService.shutdown() 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/section1/code6/Code1-6.kt: -------------------------------------------------------------------------------- 1 | package section1.code6 2 | 3 | import java.util.concurrent.CompletableFuture 4 | import java.util.concurrent.Executors 5 | 6 | fun main() { 7 | val executorService = Executors.newFixedThreadPool(2) 8 | 9 | // ExecutorService에 반환 값이 있는 작업 제출 10 | val completableFuture = CompletableFuture.supplyAsync( 11 | { 12 | Thread.sleep(2000L) 13 | return@supplyAsync "더미 결과값" 14 | }, 15 | executorService 16 | ) 17 | 18 | // 콜백 형식으로 결과값 처리 19 | completableFuture.thenAccept { result -> 20 | println("결과:${result}") 21 | } 22 | 23 | executorService.shutdown() 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/section10/code1/Code10-1.kt: -------------------------------------------------------------------------------- 1 | package section10.code1 2 | 3 | 4 | fun routine() { 5 | routineA() // routineA는 routine의 서브루틴 6 | routineB() // routineB는 routine의 서브루틴 7 | } 8 | 9 | fun routineA() { 10 | routineB() // routineB는 routineA의 서브루틴 11 | } 12 | 13 | fun routineB() { 14 | // 작업 실행 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/section10/code2/Code10-2.kt: -------------------------------------------------------------------------------- 1 | package section10.code2 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch { 7 | while(true) { 8 | println("자식 코루틴에서 작업 실행 중") 9 | yield() // 스레드 사용 권한 양보 10 | } 11 | } 12 | 13 | while(true) { 14 | println("부모 코루틴에서 작업 실행 중") 15 | yield() // 스레드 사용 권한 양보 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/section10/code3/Code10-3.kt: -------------------------------------------------------------------------------- 1 | package section10.code3 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | repeat(10) { repeatTime -> 8 | launch { 9 | delay(1000L) // 1초 동안 코루틴 일시 중단 10 | println("[${getElapsedTime(startTime)}] 코루틴${repeatTime} 실행 완료") 11 | } 12 | } 13 | } 14 | 15 | fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" 16 | -------------------------------------------------------------------------------- /src/main/kotlin/section10/code4/Code10-4.kt: -------------------------------------------------------------------------------- 1 | package section10.code4 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | repeat(10) { repeatTime -> 8 | launch { 9 | Thread.sleep(1000L) // 1초 동안 스레드 블로킹(코루틴의 스레드 점유 유지) 10 | println("[${getElapsedTime(startTime)}] 코루틴${repeatTime} 실행 완료") 11 | } 12 | } 13 | } 14 | 15 | fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" -------------------------------------------------------------------------------- /src/main/kotlin/section10/code5/Code10-5.kt: -------------------------------------------------------------------------------- 1 | package section10.code5 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val job = launch { 7 | println("1. launch 코루틴 작업이 시작됐습니다") 8 | delay(1000L) 9 | println("2. launch 코루틴 작업이 완료됐습니다") 10 | } 11 | println("3. runBlocking 코루틴이 곧 일시 중단 되고 메인 스레드가 양보됩니다") 12 | job.join() 13 | println("4. runBlocking이 메인 스레드에 보내져 작업이 다시 재개됩니다") 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/section10/code6/Code10-6.kt: -------------------------------------------------------------------------------- 1 | package section10.code6 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val job = launch { 7 | while (this.isActive) { 8 | println("작업 중") 9 | } 10 | } 11 | delay(100L) // 100밀리초간 일시 중단 12 | job.cancel() // 코루틴 취소 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section10/code7/Code10-7.kt: -------------------------------------------------------------------------------- 1 | package section10.code7 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val dispatcher = newFixedThreadPoolContext(2, "Thread") 7 | launch(dispatcher) { 8 | repeat(5) { 9 | println("[${Thread.currentThread().name}] 코루틴 실행이 일시 중단 됩니다") 10 | delay(100L) 11 | println("[${Thread.currentThread().name}] 코루틴 실행이 재개 됩니다") 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/section10/code8/Code10-8.kt: -------------------------------------------------------------------------------- 1 | package section10.code8 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val dispatcher = newFixedThreadPoolContext(2, "Thread") 7 | launch(dispatcher) { 8 | repeat(5) { 9 | println("[${Thread.currentThread().name}] 스레드를 점유한채로 100밀리초간 대기합니다") 10 | Thread.sleep(100L) 11 | println("[${Thread.currentThread().name}] 점유한 스레드에서 마저 실행됩니다") 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code1/Code11-1.kt: -------------------------------------------------------------------------------- 1 | package section11.code1 2 | 3 | import kotlinx.coroutines.* 4 | 5 | var count = 0 6 | 7 | fun main() = runBlocking { 8 | withContext(Dispatchers.Default) { 9 | repeat(10_000) { 10 | launch { 11 | count += 1 12 | } 13 | } 14 | } 15 | println("count = $count") 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code10/Code11-10.kt: -------------------------------------------------------------------------------- 1 | package section11.code10 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val job = launch { 7 | println("작업1") 8 | } 9 | job.cancel() // 실행 대기 상태의 코루틴에 취소 요청 10 | println("작업2") 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code11/Code11-11.kt: -------------------------------------------------------------------------------- 1 | package section11.code11 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val job = launch(start = CoroutineStart.ATOMIC) { 7 | println("작업1") 8 | } 9 | job.cancel() // 생성 상태의 코루틴에 취소 요청 10 | println("작업2") 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code12/Code11-12.kt: -------------------------------------------------------------------------------- 1 | package section11.code12 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(start = CoroutineStart.UNDISPATCHED) { 7 | println("작업1") 8 | } 9 | println("작업2") 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code13/Code11-13.kt: -------------------------------------------------------------------------------- 1 | package section11.code13 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(start = CoroutineStart.UNDISPATCHED) { 7 | println("일시 중단 전에는 CoroutineDispatcher을 거치지 않고 즉시 실행된다") 8 | delay(100L) 9 | println("일시 중단 후에는 CoroutineDispatcher을 거쳐 실행된다") 10 | } 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code14/Code11-14.kt: -------------------------------------------------------------------------------- 1 | package section11.code14 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking(Dispatchers.IO) { 6 | println("runBlocking 코루틴 실행 스레드: ${Thread.currentThread().name}") 7 | launch(Dispatchers.Unconfined) { 8 | println("launch 코루틴 실행 스레드: ${Thread.currentThread().name}") 9 | } 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code15/Code11-15.kt: -------------------------------------------------------------------------------- 1 | package section11.code15 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking(Dispatchers.IO) { 6 | println("runBlocking 코루틴 실행 스레드: ${Thread.currentThread().name}") 7 | launch { 8 | println("launch 코루틴 실행 스레드: ${Thread.currentThread().name}") 9 | } 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code16/Code11-16.kt: -------------------------------------------------------------------------------- 1 | package section11.code16 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | println("작업1") 7 | launch(Dispatchers.Unconfined) { 8 | println("작업2") 9 | } 10 | println("작업3") 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code17/Code11-17.kt: -------------------------------------------------------------------------------- 1 | package section11.code17 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | println("작업1") 7 | launch { 8 | println("작업2") 9 | } 10 | println("작업3") 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code18/Code11-18.kt: -------------------------------------------------------------------------------- 1 | package section11.code18 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(Dispatchers.Unconfined) { 7 | println("[Dispatchers.Unconfined] 시작 스레드: ${Thread.currentThread().name}") 8 | delay(100L) 9 | println("[Dispatchers.Unconfined] 재개 스레드: ${Thread.currentThread().name}") 10 | } 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code19/Code11-19.kt: -------------------------------------------------------------------------------- 1 | package section11.code19 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(start = CoroutineStart.UNDISPATCHED) { 7 | println("[CoroutineStart.UNDISPATCHED] 시작 스레드: ${Thread.currentThread().name}") 8 | delay(100L) 9 | println("[CoroutineStart.UNDISPATCHED] 재개 스레드: ${Thread.currentThread().name}") 10 | } 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code2/Code11-2.kt: -------------------------------------------------------------------------------- 1 | package section11.code2 2 | 3 | import kotlinx.coroutines.* 4 | 5 | @Volatile 6 | var count = 0 7 | 8 | fun main() = runBlocking { 9 | withContext(Dispatchers.Default) { 10 | repeat(10_000) { 11 | launch { 12 | count += 1 13 | } 14 | } 15 | } 16 | println("count = $count") 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code20/Code11-20.kt: -------------------------------------------------------------------------------- 1 | package section11.code20 2 | 3 | fun main() { 4 | println("[${Thread.currentThread().name}] 작업 시작") 5 | Thread.sleep(1000L) 6 | println("[${Thread.currentThread().name}] 작업 종료") 7 | } 8 | -------------------------------------------------------------------------------- /src/main/kotlin/section11/code21/Code11-21.kt: -------------------------------------------------------------------------------- 1 | package section11.code21 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.concurrent.thread 5 | import kotlin.coroutines.resume 6 | 7 | fun main() = runBlocking { 8 | val result = suspendCancellableCoroutine { continuation: CancellableContinuation -> // runBlocking 코루틴 일시 중단 시작 9 | thread { // 새로운 스레드 생성 10 | Thread.sleep(1000L) // 1초간 대기 11 | continuation.resume("실행 결과") // runBlocking 코루틴 재개 12 | } 13 | } 14 | println(result) // 코루틴 재개 시 반환 받은 결과 출력 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code22/Code11-22.kt: -------------------------------------------------------------------------------- 1 | package section11.code22 2 | 3 | import kotlinx.coroutines.CancellableContinuation 4 | import kotlinx.coroutines.runBlocking 5 | import kotlinx.coroutines.suspendCancellableCoroutine 6 | import kotlin.coroutines.resume 7 | 8 | fun main() = runBlocking { 9 | val result : Response = executeNetworkCall() 10 | println(result) // 코루틴 재개 시 반환 받은 결과 출력 11 | } 12 | 13 | suspend fun executeNetworkCall(): Response { 14 | return suspendCancellableCoroutine { continuation: CancellableContinuation -> 15 | executeNetworkCallAsync( 16 | onSuccess = { 17 | continuation.resume(Response.Success(it)) 18 | }, 19 | onFail = { 20 | continuation.resume(Response.Fail(it)) 21 | } 22 | ) 23 | } 24 | } 25 | 26 | sealed class Response() { 27 | data class Success(val string: String) : Response() 28 | data class Fail(val throwable: Throwable): Response() 29 | } 30 | 31 | fun executeNetworkCallAsync(onSuccess: (String) -> Unit, onFail: (Throwable) -> Unit) { 32 | // 네트워크 요청 실행 33 | onSuccess("네트워크 요청 성공") 34 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code3/Code11-3.kt: -------------------------------------------------------------------------------- 1 | package section11.code3 2 | 3 | import kotlinx.coroutines.* 4 | import kotlinx.coroutines.sync.Mutex 5 | 6 | var count = 0 7 | val mutex = Mutex() 8 | 9 | fun main() = runBlocking { 10 | withContext(Dispatchers.Default) { 11 | repeat(10_000) { 12 | launch { 13 | mutex.lock() 14 | count += 1 15 | mutex.unlock() 16 | } 17 | } 18 | } 19 | println("count = $count") 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code4/Code11-4.kt: -------------------------------------------------------------------------------- 1 | package section11.code4 2 | 3 | import kotlinx.coroutines.* 4 | import kotlinx.coroutines.sync.Mutex 5 | import kotlinx.coroutines.sync.withLock 6 | 7 | var count = 0 8 | val mutex = Mutex() 9 | 10 | fun main() = runBlocking { 11 | withContext(Dispatchers.Default) { 12 | repeat(10_000) { 13 | launch { 14 | mutex.withLock { 15 | count += 1 16 | } 17 | } 18 | } 19 | } 20 | println("count = $count") 21 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code5/Code11-5.kt: -------------------------------------------------------------------------------- 1 | package section11.code5 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import kotlinx.coroutines.runBlocking 6 | import kotlinx.coroutines.withContext 7 | import java.util.concurrent.locks.ReentrantLock 8 | 9 | var count = 0 10 | val reentrantLock = ReentrantLock() 11 | 12 | fun main() = runBlocking { 13 | withContext(Dispatchers.Default) { 14 | repeat(10_000) { 15 | launch { 16 | reentrantLock.lock() 17 | count += 1 18 | reentrantLock.unlock() 19 | } 20 | } 21 | } 22 | println("count = ${count}") 23 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code6/Code11-6.kt: -------------------------------------------------------------------------------- 1 | package section11.code6 2 | 3 | import kotlinx.coroutines.* 4 | 5 | var count = 0 6 | val countChangeDispatcher = newSingleThreadContext("전용 스레드") 7 | 8 | fun main() = runBlocking { 9 | withContext(Dispatchers.Default) { 10 | repeat(10_000) { 11 | launch(countChangeDispatcher) { 12 | count += 1 13 | } 14 | } 15 | } 16 | println("count = $count") 17 | } 18 | -------------------------------------------------------------------------------- /src/main/kotlin/section11/code7/Code11-7.kt: -------------------------------------------------------------------------------- 1 | package section11.code7 2 | 3 | import kotlinx.coroutines.* 4 | import java.util.concurrent.atomic.AtomicInteger 5 | 6 | var count = AtomicInteger(0) 7 | 8 | fun main() = runBlocking { 9 | withContext(Dispatchers.Default) { 10 | repeat(10_000) { 11 | launch { 12 | count.getAndUpdate { it + 1 } 13 | } 14 | } 15 | } 16 | println("count = $count") 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code8/Code11-8.kt: -------------------------------------------------------------------------------- 1 | package section11.code8 2 | 3 | import kotlinx.coroutines.* 4 | import java.util.concurrent.atomic.AtomicInteger 5 | 6 | var count = AtomicInteger(0) 7 | 8 | fun main() = runBlocking { 9 | withContext(Dispatchers.Default) { 10 | repeat(10_000) { 11 | launch { 12 | val currentCount = count.get() 13 | // 위 코드와 아래 코드의 실행 사이에 다른 스레드가 count의 값을 읽거나 변경할 수 있다. 14 | count.set(currentCount + 1) 15 | } 16 | } 17 | } 18 | println("count = $count") 19 | } -------------------------------------------------------------------------------- /src/main/kotlin/section11/code9/Code11-9.kt: -------------------------------------------------------------------------------- 1 | package section11.code9 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch { 7 | println("작업1") 8 | } 9 | println("작업2") 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/section12/code1/AddUseCase.kt: -------------------------------------------------------------------------------- 1 | package section12.code1 2 | 3 | class AddUseCase { 4 | fun add(vararg args: Int): Int { 5 | return args.sum() 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/kotlin/section12/code2/UserNameRepository.kt: -------------------------------------------------------------------------------- 1 | package section12.code2 2 | 3 | interface UserNameRepository { 4 | fun saveUserName(id: String, name: String) 5 | fun getNameByUserId(id: String): String 6 | } -------------------------------------------------------------------------------- /src/main/kotlin/section12/code2/UserPhoneNumberRepository.kt: -------------------------------------------------------------------------------- 1 | package section12.code2 2 | 3 | interface UserPhoneNumberRepository { 4 | fun saveUserPhoneNumber(id: String, phoneNumber: String) 5 | fun getPhoneNumberByUserId(id: String): String 6 | } -------------------------------------------------------------------------------- /src/main/kotlin/section12/code2/UserProfile.kt: -------------------------------------------------------------------------------- 1 | package section12.code2 2 | 3 | data class UserProfile( 4 | val id: String, 5 | val name: String, 6 | val phoneNumber: String 7 | ) -------------------------------------------------------------------------------- /src/main/kotlin/section12/code2/UserProfileFetcher.kt: -------------------------------------------------------------------------------- 1 | package section12.code2 2 | 3 | class UserProfileFetcher( 4 | private val userNameRepository: UserNameRepository, 5 | private val userPhoneNumberRepository: UserPhoneNumberRepository 6 | ) { 7 | fun getUserProfileById(id: String): UserProfile { 8 | // 유저의 이름을 UserNameRepository로부터 가져오기 9 | val userName = userNameRepository.getNameByUserId(id) 10 | // 유저의 전화번호를 UserPhoneNumberRepository로부터 가져오기 11 | val userPhoneNumber = userPhoneNumberRepository.getPhoneNumberByUserId(id) 12 | return UserProfile( 13 | id = id, 14 | name = userName, 15 | phoneNumber = userPhoneNumber 16 | ) 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/section12/code3/RepeatAddUseCase.kt: -------------------------------------------------------------------------------- 1 | package section12.code3 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.withContext 5 | 6 | class RepeatAddUseCase { 7 | suspend fun add(repeatTime: Int): Int = withContext(Dispatchers.Default) { 8 | var result = 0 9 | repeat(repeatTime) { 10 | result += 1 11 | } 12 | return@withContext result 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/section12/code4/RepeatAddWithDelayUseCase.kt: -------------------------------------------------------------------------------- 1 | package section12.code4 2 | 3 | import kotlinx.coroutines.delay 4 | 5 | class RepeatAddWithDelayUseCase { 6 | suspend fun add(repeatTime: Int): Int { 7 | var result = 0 8 | repeat(repeatTime) { 9 | delay(100L) 10 | result += 1 11 | } 12 | return result 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/section12/code6/Follower.kt: -------------------------------------------------------------------------------- 1 | package section12.code6 2 | 3 | sealed class Follower( 4 | open val id: String, 5 | open val name: String 6 | ) { 7 | data class OfficialAccount( 8 | override val id: String, 9 | override val name: String 10 | ) : Follower(id, name) 11 | 12 | data class PersonAccount( 13 | override val id: String, 14 | override val name: String 15 | ) : Follower(id, name) 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/main/kotlin/section12/code6/FollowerSearcher.kt: -------------------------------------------------------------------------------- 1 | package section12.code6 2 | 3 | import kotlinx.coroutines.async 4 | import kotlinx.coroutines.coroutineScope 5 | 6 | class FollowerSearcher( 7 | private val officialAccountRepository: OfficialAccountRepository, 8 | private val personAccountRepository: PersonAccountRepository 9 | ) { 10 | suspend fun searchByName(name: String): List = coroutineScope { 11 | val officialAccountsDeferred = async { 12 | officialAccountRepository.searchByName(name) 13 | } 14 | val personAccountsDeferred = async { 15 | personAccountRepository.searchByName(name) 16 | } 17 | 18 | return@coroutineScope listOf( 19 | *officialAccountsDeferred.await(), 20 | *personAccountsDeferred.await() 21 | ) 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/kotlin/section12/code6/OfficialAccountRepository.kt: -------------------------------------------------------------------------------- 1 | package section12.code6 2 | 3 | interface OfficialAccountRepository { 4 | suspend fun searchByName(name: String): Array 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/section12/code6/PersonAccountRepository.kt: -------------------------------------------------------------------------------- 1 | package section12.code6 2 | 3 | interface PersonAccountRepository { 4 | suspend fun searchByName(name: String): Array 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/section12/code7/StringStateHolder.kt: -------------------------------------------------------------------------------- 1 | package section12.code7 2 | 3 | import kotlinx.coroutines.* 4 | 5 | /** 6 | * 테스트 불가능한 StringStateHolder 객체 7 | */ 8 | //class StringStateHolder { 9 | // private val coroutineScope = CoroutineScope(Dispatchers.IO) 10 | // 11 | // var stringState = "" 12 | // private set 13 | // 14 | // fun updateStringWithDelay(string: String) { 15 | // coroutineScope.launch { 16 | // delay(1000L) 17 | // stringState = string 18 | // } 19 | // } 20 | //} 21 | 22 | /** 23 | * 테스트 가능한 StringStateHolder 객체 24 | */ 25 | class StringStateHolder( 26 | private val dispatcher: CoroutineDispatcher = Dispatchers.IO 27 | ) { 28 | private val coroutineScope = CoroutineScope(dispatcher) 29 | 30 | var stringState = "" 31 | private set 32 | 33 | fun updateStringWithDelay(string: String) { 34 | coroutineScope.launch { 35 | delay(1000L) 36 | stringState = string 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/kotlin/section2/code1/Code2-1.kt: -------------------------------------------------------------------------------- 1 | package section2.code1 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | println("[${Thread.currentThread().name}] runBlocking 코루틴 시작") 7 | delay(1000L) 8 | println("[${Thread.currentThread().name}] runBlocking 코루틴 종료") 9 | } -------------------------------------------------------------------------------- /src/main/kotlin/section2/code2/Code2-2.kt: -------------------------------------------------------------------------------- 1 | package section2.code2 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | println("[${Thread.currentThread().name}] runBlocking 코루틴 시작") 7 | launch { 8 | println("[${Thread.currentThread().name}] launch 코루틴 시작") 9 | delay(500L) 10 | println("[${Thread.currentThread().name}] launch 코루틴 종료") 11 | } 12 | delay(1000L) 13 | println("[${Thread.currentThread().name}] runBlocking 코루틴 종료") 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/section3/code1/Code3-1.kt: -------------------------------------------------------------------------------- 1 | package section3.code1 2 | 3 | import kotlinx.coroutines.* 4 | 5 | val singleThreadDispatcher: CoroutineDispatcher = 6 | newSingleThreadContext("SingleThread") 7 | 8 | fun main() = runBlocking { 9 | launch(singleThreadDispatcher) { 10 | println("[${Thread.currentThread().name}] 실행") 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/section3/code2/Code3-2.kt: -------------------------------------------------------------------------------- 1 | package section3.code2 2 | 3 | import kotlinx.coroutines.* 4 | 5 | val multiThreadDispatcher: CoroutineDispatcher = 6 | newFixedThreadPoolContext(2, "MultiThread") 7 | 8 | fun main() = runBlocking { 9 | launch(multiThreadDispatcher) { 10 | println("[${Thread.currentThread().name}] 실행") 11 | } 12 | launch(multiThreadDispatcher) { 13 | println("[${Thread.currentThread().name}] 실행") 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/section3/code3/Code3-3.kt: -------------------------------------------------------------------------------- 1 | package section3.code3 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(Dispatchers.IO) { 7 | println("[${Thread.currentThread().name}] 작업1 실행") 8 | } 9 | launch(Dispatchers.IO) { 10 | println("[${Thread.currentThread().name}] 작업2 실행") 11 | } 12 | launch(Dispatchers.IO) { 13 | println("[${Thread.currentThread().name}] 작업3 실행") 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/main/kotlin/section3/code4/Code3-4.kt: -------------------------------------------------------------------------------- 1 | package section3.code4 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(Dispatchers.IO) { 7 | launch { 8 | println("[${Thread.currentThread().name}] 작업1 실행") 9 | } 10 | launch { 11 | println("[${Thread.currentThread().name}] 작업2 실행") 12 | } 13 | launch { 14 | println("[${Thread.currentThread().name}] 작업3 실행") 15 | } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/main/kotlin/section3/code5/Code3-5.kt: -------------------------------------------------------------------------------- 1 | package section3.code5 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(Dispatchers.Default) { 7 | launch { 8 | println("[${Thread.currentThread().name}] 작업1 실행") 9 | } 10 | launch { 11 | println("[${Thread.currentThread().name}] 작업2 실행") 12 | } 13 | launch { 14 | println("[${Thread.currentThread().name}] 작업3 실행") 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/kotlin/section3/code6/Code3-6.kt: -------------------------------------------------------------------------------- 1 | package section3.code6 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val imageProcessingDispatcher = Dispatchers.Default.limitedParallelism(2) 7 | repeat(1000) { 8 | launch(imageProcessingDispatcher) { 9 | Thread.sleep(1000L) // 이미지 처리 작업 10 | println("[${Thread.currentThread().name}] 이미지 처리 완료") 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/section3/code7/Code3-7.kt: -------------------------------------------------------------------------------- 1 | package section3.code7 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val dedicatedDispatcher = Dispatchers.IO.limitedParallelism(2) 7 | repeat(100) { 8 | launch(dedicatedDispatcher) { 9 | println("[${Thread.currentThread().name}] 중요 작업 실행") 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/kotlin/section3/code8/Code3-8.kt: -------------------------------------------------------------------------------- 1 | package section3.code8 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import kotlinx.coroutines.runBlocking 6 | 7 | fun main() = runBlocking { 8 | launch(Dispatchers.Main) { 9 | println("[${Thread.currentThread().name}] 작업 실행") 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/section4/code1/Code4-1.kt: -------------------------------------------------------------------------------- 1 | package section4.code1 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val job: Job = launch { 7 | println("[${Thread.currentThread().name}] 실행") 8 | } 9 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code10/Code4-10.kt: -------------------------------------------------------------------------------- 1 | package section4.code10 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | val longJob: Job = launch(Dispatchers.Default) { 8 | repeat(10) { repeatTime -> 9 | delay(1000L) // 1000밀리초 대기 10 | println("[${getElapsedTime(startTime)}] 반복횟수 ${repeatTime}") 11 | } 12 | } 13 | delay(2500L) // 2500밀리초 대기 14 | longJob.cancel() // 취소 15 | } 16 | 17 | fun getElapsedTime(startTime: Long): String = 18 | "지난 시간: ${System.currentTimeMillis() - startTime}밀리초" -------------------------------------------------------------------------------- /src/main/kotlin/section4/code11/Code4-11.kt: -------------------------------------------------------------------------------- 1 | package section4.code11 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val longJob: Job = launch(Dispatchers.Default) { 7 | Thread.sleep(1000L) 8 | println("longJob 코루틴의 동작") 9 | } 10 | longJob.cancel() // longJob 취소 요청 11 | executeAfterJobCancelled() // 취소 후 실행돼야 하는 동작 12 | } 13 | 14 | fun executeAfterJobCancelled() { 15 | println("longJob 코루틴 취소 후 실행돼야 하는 동작") 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code12/Code4-12.kt: -------------------------------------------------------------------------------- 1 | package section4.code12 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val longJob: Job = launch(Dispatchers.Default) { 7 | Thread.sleep(1000L) 8 | println("longJob 코루틴의 동작") 9 | } 10 | longJob.cancelAndJoin() // longJob 취소 요청 후 취소 완료될 때까지 호출 코루틴 일시 중단 11 | executeAfterJobCancelled() // 취소 후 실행돼야 하는 동작 12 | } 13 | 14 | fun executeAfterJobCancelled() { 15 | println("longJob 코루틴 취소 후 실행돼야 하는 동작") 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code13/Code4-13.kt: -------------------------------------------------------------------------------- 1 | package section4.code13 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val longJob: Job = launch(Dispatchers.Default) { 7 | delay(1000L) 8 | println("longJob 코루틴의 동작") 9 | } 10 | longJob.cancelAndJoin() // longJob 취소 요청 후 취소 완료될 때까지 호출 코루틴 일시 중단 11 | executeAfterJobCancelled() // 취소 후 실행돼야 하는 동작 12 | } 13 | 14 | fun executeAfterJobCancelled() { 15 | println("longJob 코루틴 취소 후 실행돼야 하는 동작") 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code14/Code4-14.kt: -------------------------------------------------------------------------------- 1 | package section4.code14 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val whileJob: Job = launch(Dispatchers.Default) { 7 | while(true) { 8 | println("작업 중") 9 | } 10 | } 11 | delay(100L) // 100밀리초 대기 12 | whileJob.cancel() // 코루틴 취소 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code15/Code4-15.kt: -------------------------------------------------------------------------------- 1 | package section4.code15 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val whileJob: Job = launch(Dispatchers.Default) { 7 | while(true) { 8 | println("작업 중") 9 | delay(1L) 10 | } 11 | } 12 | delay(100L) 13 | whileJob.cancel() 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/section4/code16/Code4-16.kt: -------------------------------------------------------------------------------- 1 | package section4.code16 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val whileJob: Job = launch(Dispatchers.Default) { 7 | while(true) { 8 | println("작업 중") 9 | yield() 10 | } 11 | } 12 | delay(100L) 13 | whileJob.cancel() 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code17/Code4-17.kt: -------------------------------------------------------------------------------- 1 | package section4.code17 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val whileJob: Job = launch(Dispatchers.Default) { 7 | while(this.isActive) { 8 | println("작업 중") 9 | } 10 | } 11 | delay(100L) 12 | whileJob.cancel() 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code18/Code4-18.kt: -------------------------------------------------------------------------------- 1 | package section4.code18 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val job: Job = launch { 7 | delay(1000L) 8 | } 9 | println(job) 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/section4/code19/Code4-19.kt: -------------------------------------------------------------------------------- 1 | package section4.code19 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val job: Job = launch(start = CoroutineStart.LAZY) { 7 | delay(1000L) 8 | } 9 | println(job) 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code2/Code4-2.kt: -------------------------------------------------------------------------------- 1 | package section4.code2 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val updateTokenJob = launch(Dispatchers.IO) { 7 | println("[${Thread.currentThread().name}] 토큰 업데이트 시작") 8 | delay(100L) // 새로운 토큰을 가져오는데 걸리는 시간 9 | println("[${Thread.currentThread().name}] 토큰 업데이트 완료") 10 | } 11 | 12 | val networkCallJob = launch(Dispatchers.IO) { 13 | println("[${Thread.currentThread().name}] 네트워크 요청") 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code20/Code4-20.kt: -------------------------------------------------------------------------------- 1 | package section4.code20 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val job: Job = launch { 7 | delay(1000L) 8 | } 9 | job.join() // launch 코루틴이 실행 완료될때까지 일시 중단 10 | println(job) 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code21/Code4-21.kt: -------------------------------------------------------------------------------- 1 | package section4.code21 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val whileJob: Job = launch(Dispatchers.IO) { 7 | while(true){ 8 | // 작업 실행 9 | } 10 | } 11 | whileJob.cancel() // 코루틴 취소 요청 12 | println(whileJob) 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code22/Code4-22.kt: -------------------------------------------------------------------------------- 1 | package section4.code22 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val whileJob: Job = launch(Dispatchers.IO) { 7 | while(true){ 8 | yield() // 스레드 양보(일시 중단) 9 | } 10 | } 11 | whileJob.cancelAndJoin() // 코루틴 취소 완료될때까지 대기 12 | println(whileJob) 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code23/Code4-23.kt: -------------------------------------------------------------------------------- 1 | package section4.code23 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val job: Job = launch(start = CoroutineStart.LAZY) { 7 | delay(1000L) 8 | } 9 | println(job) 10 | printJobState(job) 11 | } 12 | 13 | private fun printJobState(job: Job) { 14 | println( 15 | "Job State\n" + 16 | "isActive >> ${job.isActive}\n" + 17 | "isCancelled >> ${job.isCancelled}\n" + 18 | "isCompleted >> ${job.isCompleted} " 19 | ) 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code24/Code4-24.kt: -------------------------------------------------------------------------------- 1 | package section4.code24 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val job: Job = launch { 7 | delay(1000L) 8 | } 9 | println(job) 10 | printJobState(job) 11 | } 12 | 13 | private fun printJobState(job: Job) { 14 | println( 15 | "Job State\n" + 16 | "isActive >> ${job.isActive}\n" + 17 | "isCancelled >> ${job.isCancelled}\n" + 18 | "isCompleted >> ${job.isCompleted} " 19 | ) 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code25/Code4-25.kt: -------------------------------------------------------------------------------- 1 | package section4.code25 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val job: Job = launch { 7 | delay(1000L) 8 | } 9 | job.join() // launch 코루틴이 실행 완료될때까지 일시 중단 10 | println(job) 11 | printJobState(job) 12 | } 13 | 14 | 15 | private fun printJobState(job: Job) { 16 | println( 17 | "Job State\n" + 18 | "isActive >> ${job.isActive}\n" + 19 | "isCancelled >> ${job.isCancelled}\n" + 20 | "isCompleted >> ${job.isCompleted} " 21 | ) 22 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code26/Code4-26.kt: -------------------------------------------------------------------------------- 1 | package section4.code26 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val whileJob: Job = launch(Dispatchers.IO) { 7 | while(true){ 8 | // 작업 실행 9 | } 10 | } 11 | whileJob.cancel() // 코루틴 취소 요청 12 | println(whileJob) 13 | printJobState(whileJob) 14 | } 15 | 16 | private fun printJobState(job: Job) { 17 | println( 18 | "Job State\n" + 19 | "isActive >> ${job.isActive}\n" + 20 | "isCancelled >> ${job.isCancelled}\n" + 21 | "isCompleted >> ${job.isCompleted} " 22 | ) 23 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code27/Code4-27.kt: -------------------------------------------------------------------------------- 1 | package section4.code27 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val whileJob: Job = launch(Dispatchers.IO) { 7 | while(true){ 8 | yield() // 스레드 양보(일시 중단) 9 | } 10 | } 11 | whileJob.cancelAndJoin() // 코루틴 취소 완료될때까지 대기 12 | println(whileJob) 13 | printJobState(whileJob) 14 | } 15 | 16 | private fun printJobState(job: Job) { 17 | println( 18 | "Job State\n" + 19 | "isActive >> ${job.isActive}\n" + 20 | "isCancelled >> ${job.isCancelled}\n" + 21 | "isCompleted >> ${job.isCompleted} " 22 | ) 23 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code3/Code4-3.kt: -------------------------------------------------------------------------------- 1 | package section4.code3 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val updateTokenJob = launch(Dispatchers.IO) { 7 | println("[${Thread.currentThread().name}] 토큰 업데이트 시작") 8 | delay(100L) 9 | println("[${Thread.currentThread().name}] 토큰 업데이트 완료") 10 | } 11 | updateTokenJob.join() // networkCallJob 실행 전 updateTokenJob.join() 호출 12 | 13 | val networkCallJob = launch(Dispatchers.IO) { 14 | println("[${Thread.currentThread().name}] 네트워크 요청") 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code4/Code4-4.kt: -------------------------------------------------------------------------------- 1 | package section4.code4 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val convertImageJob1: Job = launch(Dispatchers.Default) { 7 | Thread.sleep(1000L) // 이미지1 변환 작업 실행 8 | println("[${Thread.currentThread().name}] 이미지1 변환 완료") 9 | } 10 | val convertImageJob2: Job = launch(Dispatchers.Default) { 11 | Thread.sleep(1000L) // 이미지2 변환 작업 실행 12 | println("[${Thread.currentThread().name}] 이미지2 변환 완료") 13 | } 14 | 15 | convertImageJob1.join() // 이미지 1이 변환될 때까지 대기 16 | convertImageJob2.join() // 이미지 2가 변환될 때까지 대기 17 | 18 | val uploadImageJob: Job = launch(Dispatchers.IO) { 19 | println("[${Thread.currentThread().name}] 이미지1,2 업로드") 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code5/Code4-5.kt: -------------------------------------------------------------------------------- 1 | package section4.code5 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val convertImageJob1: Job = launch(Dispatchers.Default) { 7 | Thread.sleep(1000L) // 이미지1 변환 작업 실행 8 | println("[${Thread.currentThread().name}] 이미지1 변환 완료") 9 | } 10 | val convertImageJob2: Job = launch(Dispatchers.Default) { 11 | Thread.sleep(1000L) // 이미지2 변환 작업 실행 12 | println("[${Thread.currentThread().name}] 이미지2 변환 완료") 13 | } 14 | 15 | joinAll(convertImageJob1, convertImageJob2) // 이미지 1,2가 모두 변환될 때까지 대기 16 | 17 | listOf(convertImageJob1, convertImageJob2).joinAll() 18 | 19 | val uploadImageJob: Job = launch(Dispatchers.IO) { 20 | println("[${Thread.currentThread().name}] 이미지1,2 업로드") 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/kotlin/section4/code6/Code4-6.kt: -------------------------------------------------------------------------------- 1 | package section4.code6 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | val immediateJob: Job = launch { 8 | println("[${getElapsedTime(startTime)}] launch 코루틴 실행") 9 | } 10 | } 11 | 12 | fun getElapsedTime(startTime: Long): String = 13 | "지난 시간: ${System.currentTimeMillis() - startTime}밀리초" -------------------------------------------------------------------------------- /src/main/kotlin/section4/code7/Code4-7.kt: -------------------------------------------------------------------------------- 1 | package section4.code7 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | val lazyJob: Job = launch(start = CoroutineStart.LAZY) { 8 | println("[${getElapsedTime(startTime)}] launch 코루틴 지연 실행") 9 | } 10 | } 11 | 12 | private fun getElapsedTime(startTime: Long): String = 13 | "지난 시간: ${System.currentTimeMillis() - startTime}밀리초" 14 | 15 | -------------------------------------------------------------------------------- /src/main/kotlin/section4/code8/Code4-8.kt: -------------------------------------------------------------------------------- 1 | package section4.code8 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | val lazyJob: Job = launch(start = CoroutineStart.LAZY) { 8 | println("[${getElapsedTime(startTime)}] launch 코루틴 지연 실행") 9 | } 10 | delay(3000L) // 3000밀리초간 대기 11 | lazyJob.start() // 코루틴 실행 lazyJob.join()을 호출해도 실행됨 12 | } 13 | 14 | 15 | fun getElapsedTime(startTime: Long): String = 16 | "지난 시간: ${System.currentTimeMillis() - startTime}밀리초" -------------------------------------------------------------------------------- /src/main/kotlin/section4/code9/Code4-9.kt: -------------------------------------------------------------------------------- 1 | package section4.code9 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | val longJob: Job = launch(Dispatchers.Default) { 8 | repeat(10) { repeatTime -> 9 | delay(1000L) // 1000밀리초 대기 10 | println("[${getElapsedTime(startTime)}] 반복횟수 ${repeatTime}") 11 | } 12 | } 13 | } 14 | 15 | fun getElapsedTime(startTime: Long): String = 16 | "지난 시간: ${System.currentTimeMillis() - startTime}밀리초" -------------------------------------------------------------------------------- /src/main/kotlin/section5/code1/Code5-1.kt: -------------------------------------------------------------------------------- 1 | package section5.code1 2 | import kotlinx.coroutines.* 3 | 4 | fun main() = runBlocking { 5 | val networkDeferred = async(Dispatchers.IO) { 6 | delay(1000L) // 네트워크 요청 7 | return@async "Dummy Response" // Dummy Response 반환 8 | } 9 | } -------------------------------------------------------------------------------- /src/main/kotlin/section5/code10/Code5-10.kt: -------------------------------------------------------------------------------- 1 | package section5.code10 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() // 1. 시작 시간 기록 7 | 8 | // 2. 플랫폼1에서 등록한 관람객 목록을 가져오는 코루틴 실행 9 | val participantDeferred1: Deferred> = async(Dispatchers.IO) { 10 | delay(1000L) 11 | return@async arrayOf("철수", "영수") 12 | } 13 | 14 | // 3. 플랫폼2에서 등록한 관람객 목록을 가져오는 코루틴 실행 15 | val participantDeferred2: Deferred> = async(Dispatchers.IO) { 16 | delay(1000L) 17 | return@async arrayOf("영희") 18 | } 19 | 20 | // 4. 두 개의 코루틴으로부터 결과가 수신될 때까지 대기 21 | val results = listOf(participantDeferred1, participantDeferred2).awaitAll() 22 | 23 | // 5. 지난 시간 표시 및 참여자 목록을 병합해 출력 24 | println("[${getElapsedTime(startTime)}] 참여자 목록: ${listOf(*results[0], *results[1])}") 25 | } 26 | 27 | private fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" -------------------------------------------------------------------------------- /src/main/kotlin/section5/code11/Code5-11.kt: -------------------------------------------------------------------------------- 1 | package section5.code11 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val result: String = withContext(Dispatchers.IO) { 7 | delay(1000L) // 네트워크 요청 8 | println("[${Thread.currentThread().name}] 결과값이 반환됩니다") 9 | return@withContext "결과값" // 결과값 반환 10 | } 11 | println("[${Thread.currentThread().name}] $result") // 결과값 출력 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/section5/code12/Code5-12.kt: -------------------------------------------------------------------------------- 1 | package section5.code12 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val result: String = async(Dispatchers.IO) { 7 | delay(1000L) // 네트워크 요청 8 | println("[${Thread.currentThread().name}] 결과값이 반환됩니다") 9 | "결과값" // 결과값 반환 10 | }.await() 11 | println("[${Thread.currentThread().name}] $result") // 결과값 출력 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/section5/code13/Code5-13.kt: -------------------------------------------------------------------------------- 1 | package section5.code13 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val result1: String = withContext(Dispatchers.IO) { 7 | delay(1000L) // 네트워크 요청 8 | "결과값1" // 결과값 반환 9 | } 10 | val result2: String = withContext(Dispatchers.IO) { 11 | delay(1000L) // 네트워크 요청 12 | "결과값1" // 결과값 반환 13 | } 14 | 15 | val results = listOf(result1, result2) 16 | println("[${Thread.currentThread().name}] ${results.joinToString(", ")}") // 결과값 출력 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/section5/code14/Code5-14.kt: -------------------------------------------------------------------------------- 1 | package section5.code14 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val networkDeferred1: Deferred = async(Dispatchers.IO) { 7 | delay(1000L) // 네트워크 요청 8 | "결과값1" // 결과값 반환 9 | } 10 | val networkDeferred2: Deferred = async(Dispatchers.IO) { 11 | delay(1000L) // 네트워크 요청 12 | "결과값2" // 결과값 반환 13 | } 14 | 15 | val results: List = awaitAll(networkDeferred1, networkDeferred2) 16 | println("[${Thread.currentThread().name}] ${results.joinToString(", ")}") // 결과값 출력 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/section5/code15/Code5-15.kt: -------------------------------------------------------------------------------- 1 | package section5.code15 2 | 3 | import kotlinx.coroutines.* 4 | 5 | private val myDispatcher1 = newSingleThreadContext("MyThread1") 6 | private val myDispatcher2 = newSingleThreadContext("MyThread2") 7 | 8 | fun main() = runBlocking { 9 | println("[${Thread.currentThread().name}] 코루틴 실행1") 10 | withContext(myDispatcher1) { 11 | println("[${Thread.currentThread().name}] 코루틴 실행2") 12 | withContext(myDispatcher2) { 13 | println("[${Thread.currentThread().name}] 코루틴 실행3") 14 | } 15 | println("[${Thread.currentThread().name}] 코루틴 실행4") 16 | } 17 | println("[${Thread.currentThread().name}] 코루틴 실행5") 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/section5/code2/Code5-2.kt: -------------------------------------------------------------------------------- 1 | package section5.code2 2 | import kotlinx.coroutines.* 3 | 4 | fun main() = runBlocking { 5 | val networkDeferred: Deferred = async(Dispatchers.IO) { 6 | delay(1000L) // 네트워크 요청 7 | return@async "Dummy Response" // Dummy Response 반환 8 | } 9 | val result = networkDeferred.await() // 결과값이 반환될 때까지 runBlocking 일시 중단 10 | println(result) // 결과값 출력 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/section5/code3/Code5-3.kt: -------------------------------------------------------------------------------- 1 | package section5.code3 2 | import kotlinx.coroutines.* 3 | 4 | fun main() = runBlocking { 5 | val networkDeferred: Deferred = async(Dispatchers.IO) { 6 | delay(1000L) // 네트워크 요청 7 | return@async "Dummy Response" // 결과값 반환 8 | } 9 | val result = networkDeferred.await() // networkDeferred로부터 결과값이 반환될 때까지 runBlocking 일시 중단 10 | println(result) // Dummy Response 출력 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/section5/code4/Code5-4.kt: -------------------------------------------------------------------------------- 1 | package section5.code4 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val networkDeferred: Deferred = async(Dispatchers.IO) { 7 | delay(1000L) 8 | return@async "Dummy Response" 9 | } 10 | networkDeferred.join() // networkDeferred가 완료될 때까지 runBlocking 일시 중단 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/section5/code5/Code5-5.kt: -------------------------------------------------------------------------------- 1 | package section5.code5 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val networkDeferred: Deferred = async(Dispatchers.IO) { 7 | delay(1000L) 8 | return@async "Dummy Response" 9 | } 10 | networkDeferred.cancel() // networkDeferred 코루틴 취소 요청 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/section5/code6/Code5-6.kt: -------------------------------------------------------------------------------- 1 | package section5.code6 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val networkDeferred: Deferred = async(Dispatchers.IO) { 7 | delay(1000L) 8 | return@async "Dummy Response" 9 | } 10 | networkDeferred.join() 11 | printJobState(job = networkDeferred) 12 | } 13 | 14 | private fun printJobState(job: Job) { 15 | println( 16 | "Job State\n" + 17 | "isActive >> ${job.isActive}\n" + 18 | "isCancelled >> ${job.isCancelled}\n" + 19 | "isCompleted >> ${job.isCompleted} " 20 | ) 21 | } -------------------------------------------------------------------------------- /src/main/kotlin/section5/code7/Code5-7.kt: -------------------------------------------------------------------------------- 1 | package section5.code7 2 | import kotlinx.coroutines.* 3 | 4 | fun main() = runBlocking { 5 | val startTime = System.currentTimeMillis() // 1. 시작 시간 기록 6 | 7 | // 2. 플랫폼1에서 등록한 관람객 목록을 가져오는 코루틴 실행 8 | val participantDeferred1: Deferred> = async(Dispatchers.IO) { 9 | delay(1000L) 10 | return@async arrayOf("철수","영수") 11 | } 12 | val participants1 = participantDeferred1.await() // 3. 결과가 수신 될 때까지 대기 13 | 14 | // 4. 플랫폼2에서 등록한 관람객 목록을 가져오는 코루틴 실행 15 | val participantDeferred2: Deferred> = async(Dispatchers.IO) { 16 | delay(1000L) 17 | return@async arrayOf("영희") 18 | } 19 | val participants2 = participantDeferred2.await() // 5. 결과가 수신 될 때까지 대기 20 | 21 | // 6. 지난 시간 표시 및 참여자 목록을 병합해 출력 22 | println("[${getElapsedTime(startTime)}] 참여자 목록: ${listOf(*participants1, *participants2)}") 23 | } 24 | 25 | fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" 26 | -------------------------------------------------------------------------------- /src/main/kotlin/section5/code8/Code5-8.kt: -------------------------------------------------------------------------------- 1 | package section5.code8 2 | import kotlinx.coroutines.* 3 | 4 | fun main() = runBlocking { 5 | val startTime = System.currentTimeMillis() // 1. 시작 시간 기록 6 | 7 | // 2. 플랫폼1에서 등록한 관람객 목록을 가져오는 코루틴 실행 8 | val participantDeferred1: Deferred> = async(Dispatchers.IO) { 9 | delay(1000L) 10 | return@async arrayOf("철수","영수") 11 | } 12 | 13 | // 3. 플랫폼2에서 등록한 관람객 목록을 가져오는 코루틴 실행 14 | val participantDeferred2: Deferred> = async(Dispatchers.IO) { 15 | delay(1000L) 16 | return@async arrayOf("영희") 17 | } 18 | 19 | val participants1 = participantDeferred1.await() // 4. 결과가 수신 될 때까지 대기 20 | val participants2 = participantDeferred2.await() // 5. 결과가 수신 될 때까지 대기 21 | 22 | // 6. 지난 시간 표시 및 참여자 목록을 병합해 출력 23 | println("[${getElapsedTime(startTime)}] 참여자 목록: ${listOf(*participants1, *participants2)}") 24 | } 25 | 26 | private fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" 27 | -------------------------------------------------------------------------------- /src/main/kotlin/section5/code9/Code5-9.kt: -------------------------------------------------------------------------------- 1 | package section5.code9 2 | 3 | import kotlinx.coroutines.* 4 | 5 | 6 | fun main() = runBlocking { 7 | val startTime = System.currentTimeMillis() // 1. 시작 시간 기록 8 | 9 | // 2. 플랫폼1에서 등록한 관람객 목록을 가져오는 코루틴 실행 10 | val participantDeferred1: Deferred> = async(Dispatchers.IO) { 11 | delay(1000L) 12 | return@async arrayOf("철수", "영수") 13 | } 14 | 15 | // 3. 플랫폼2에서 등록한 관람객 목록을 가져오는 코루틴 실행 16 | val participantDeferred2: Deferred> = async(Dispatchers.IO) { 17 | delay(1000L) 18 | return@async arrayOf("영희") 19 | } 20 | 21 | // 4. 두 개의 코루틴으로부터 결과가 수신될 때까지 대기 22 | val results = awaitAll(participantDeferred1, participantDeferred2) 23 | 24 | // 5. 지난 시간 표시 및 참여자 목록을 병합해 출력 25 | println("[${getElapsedTime(startTime)}] 참여자 목록: ${listOf(*results[0], *results[1])}") 26 | } 27 | 28 | private fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" 29 | -------------------------------------------------------------------------------- /src/main/kotlin/section6/code1/Code6-1.kt: -------------------------------------------------------------------------------- 1 | package section6.code1 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val coroutineName: CoroutineName = CoroutineName("코루틴 이름 설정") 7 | val dispatcher: CoroutineDispatcher = Dispatchers.IO 8 | val job: Job = Job() 9 | val coroutineExceptionHandler: CoroutineExceptionHandler = CoroutineExceptionHandler( 10 | handler = { coroutineContext, throwable -> 11 | // 예외 처리 12 | } 13 | ) 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/section6/code10/Code6-10.kt: -------------------------------------------------------------------------------- 1 | package section6.code10 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.coroutines.CoroutineContext 5 | 6 | fun main() = runBlocking { 7 | val coroutineName = CoroutineName("MyCoroutine") 8 | val dispatcher = Dispatchers.IO 9 | val job = Job() 10 | val coroutineContext: CoroutineContext = coroutineName + dispatcher + job 11 | 12 | val deletedCoroutineContext = coroutineContext.minusKey(CoroutineName) 13 | 14 | println("coroutineContext: $coroutineContext") 15 | println("deletedCoroutineContext: $deletedCoroutineContext") 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/section6/code2/Code6-2.kt: -------------------------------------------------------------------------------- 1 | package section6.code2 2 | 3 | import kotlinx.coroutines.* 4 | 5 | @OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class) 6 | fun main() = runBlocking { 7 | val coroutineContext = newSingleThreadContext("MyThread") + CoroutineName("MyCoroutine") 8 | 9 | launch(context = coroutineContext) { 10 | println("[${Thread.currentThread().name}] 실행") 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/section6/code3/Code6-3.kt: -------------------------------------------------------------------------------- 1 | package section6.code3 2 | 3 | import kotlinx.coroutines.* 4 | 5 | @OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class) 6 | fun main() = runBlocking { 7 | val coroutineContext = newSingleThreadContext("MyThread") + CoroutineName("MyCoroutine") 8 | val newCoroutineContext = coroutineContext + CoroutineName("NewCoroutine") 9 | 10 | launch(context = newCoroutineContext) { 11 | println("[${Thread.currentThread().name}] 실행") 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section6/code4/Code6-4.kt: -------------------------------------------------------------------------------- 1 | package section6.code4 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val coroutineContext1 = CoroutineName("MyCoroutine1") + newSingleThreadContext("MyThread1") 7 | val coroutineContext2 = CoroutineName("MyCoroutine2") + newSingleThreadContext("MyThread2") 8 | val combinedCoroutineContext = coroutineContext1 + coroutineContext2 9 | 10 | launch(context = combinedCoroutineContext) { 11 | println("[${Thread.currentThread().name}] 실행") 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section6/code5/Code6-5.kt: -------------------------------------------------------------------------------- 1 | package section6.code5 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.coroutines.CoroutineContext 5 | 6 | val myJob = Job() 7 | val coroutineContext: CoroutineContext = Dispatchers.Default + myJob 8 | -------------------------------------------------------------------------------- /src/main/kotlin/section6/code6/Code6-6.kt: -------------------------------------------------------------------------------- 1 | package section6.code6 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val coroutineContext = CoroutineName("MyCoroutine") + Dispatchers.IO 7 | val nameFromContext = coroutineContext[CoroutineName.Key] 8 | println(nameFromContext) 9 | } -------------------------------------------------------------------------------- /src/main/kotlin/section6/code7/Code6-7.kt: -------------------------------------------------------------------------------- 1 | package section6.code7 2 | 3 | import kotlinx.coroutines.* 4 | 5 | @OptIn(ExperimentalStdlibApi::class) 6 | fun main() = runBlocking { 7 | val coroutineContext = CoroutineName("MyCoroutine") + Dispatchers.IO 8 | val nameFromContext = coroutineContext[CoroutineDispatcher] // '.Key'제거 9 | println(nameFromContext) 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/section6/code8/Code6-8.kt: -------------------------------------------------------------------------------- 1 | package section6.code8 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val coroutineName : CoroutineName = CoroutineName("MyCoroutine") 7 | val dispatcher : CoroutineDispatcher = Dispatchers.IO 8 | val coroutineContext = coroutineName + dispatcher 9 | 10 | println(coroutineContext[coroutineName.key]) 11 | println(coroutineContext[dispatcher.key]) 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/section6/code9/Code6-9.kt: -------------------------------------------------------------------------------- 1 | package section6.code9 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.coroutines.CoroutineContext 5 | 6 | fun main() = runBlocking { 7 | val coroutineName = CoroutineName("MyCoroutine") 8 | val dispatcher = Dispatchers.IO 9 | val myJob = Job() 10 | val coroutineContext: CoroutineContext = coroutineName + dispatcher + myJob 11 | 12 | val deletedCoroutineContext = coroutineContext.minusKey(CoroutineName) 13 | 14 | println(deletedCoroutineContext) 15 | } 16 | -------------------------------------------------------------------------------- /src/main/kotlin/section7/code1/Code7-1.kt: -------------------------------------------------------------------------------- 1 | package section7.code1 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch { // 부모 코루틴 7 | println("부모 코루틴 실행") 8 | launch { // 자식 코루틴 9 | println("자식 코루틴 실행") 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code10/Code7-10.kt: -------------------------------------------------------------------------------- 1 | package section7.code10 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | val parentJob = launch { // 부모 코루틴 생성 8 | launch { // 자식 코루틴 생성 9 | delay(1000L) // 1초간 대기 10 | println("[${getElapsedTime(startTime)}] 자식 코루틴 실행 완료") 11 | } 12 | println("[${getElapsedTime(startTime)}] 부모 코루틴이 실행하는 마지막 코드") 13 | } 14 | parentJob.invokeOnCompletion { // 부모 코루틴이 종료될 시 호출되는 콜백 등록 15 | println("[${getElapsedTime(startTime)}] 부모 코루틴 실행 완료") 16 | } 17 | delay(500L) // 500밀리초간 대기 18 | println(parentJob) // parentJob Debug String 출력 19 | printJobState(parentJob) // parentJob 상태 출력 20 | } 21 | 22 | private fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" 23 | 24 | private fun printJobState(job: Job) { 25 | println( 26 | "Job State\n" + 27 | "isActive >> ${job.isActive}\n" + 28 | "isCancelled >> ${job.isCancelled}\n" + 29 | "isCompleted >> ${job.isCompleted} " 30 | ) 31 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code11/Code7-11.kt: -------------------------------------------------------------------------------- 1 | package section7.code11 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.coroutines.CoroutineContext 5 | 6 | class CustomCoroutineScope : CoroutineScope { 7 | override val coroutineContext: CoroutineContext = Job() + 8 | newSingleThreadContext("CustomScopeThread") 9 | } 10 | 11 | fun main() { 12 | val coroutineScope = CustomCoroutineScope() // CustomCoroutineScope 인스턴스화 13 | coroutineScope.launch { 14 | delay(100L) // 100밀리초 대기 15 | println("[${Thread.currentThread().name}] 코루틴 실행 완료") 16 | } 17 | Thread.sleep(1000L) // 코드 종료 방지 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code12/Code7-12.kt: -------------------------------------------------------------------------------- 1 | package section7.code12 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() { 6 | val coroutineScope = CoroutineScope(Dispatchers.IO) 7 | coroutineScope.launch { 8 | delay(100L) // 100밀리초 대기 9 | println("[${Thread.currentThread().name}] 코루틴 실행 완료") 10 | } 11 | Thread.sleep(1000L) 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code13/Code7-13.kt: -------------------------------------------------------------------------------- 1 | package section7.code13 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() { 6 | val newScope = CoroutineScope(CoroutineName("MyCoroutine") + Dispatchers.IO) 7 | newScope.launch(context = CoroutineName("LaunchCoroutine")) { 8 | println("newScope의 coroutineContext: ${newScope.coroutineContext}") 9 | println("launch 코루틴의 coroutineContext: ${this.coroutineContext}") 10 | println("launch 코루틴의 parentJob: ${this.coroutineContext[Job]?.parent}") 11 | } 12 | Thread.sleep(1000L) 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code14/Code7-14.kt: -------------------------------------------------------------------------------- 1 | package section7.code14 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() { 6 | val newScope = CoroutineScope(CoroutineName("MyCoroutine") + Dispatchers.IO) 7 | newScope.launch(CoroutineName("LaunchCoroutine")) { 8 | this.launch { // CoroutineScope으로부터 LaunchCoroutine의 실행 환경을 제공 받아 코루틴 실행 9 | // 작업 실행 10 | } 11 | } 12 | Thread.sleep(1000L) 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code15/Code7-15.kt: -------------------------------------------------------------------------------- 1 | package section7.code15 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() { 6 | val newScope = CoroutineScope(CoroutineName("MyCoroutine") + Dispatchers.IO) 7 | newScope.launch(CoroutineName("LaunchCoroutine")) { 8 | launch { // CoroutineScope으로부터 LaunchCoroutine의 실행 환경을 제공 받아 코루틴 실행 9 | // 작업 실행 10 | } 11 | } 12 | Thread.sleep(1000L) 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code16/Code7-16.kt: -------------------------------------------------------------------------------- 1 | package section7.code16 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | this.launch { 7 | this.async { 8 | // 작업 실행 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code17/Code7-17.kt: -------------------------------------------------------------------------------- 1 | package section7.code17 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(CoroutineName("Coroutine1")) { 7 | launch(CoroutineName("Coroutine3")) { 8 | println("[${Thread.currentThread().name}] 코루틴 실행") 9 | } 10 | launch(CoroutineName("Coroutine4")) { 11 | println("[${Thread.currentThread().name}] 코루틴 실행") 12 | } 13 | } 14 | 15 | launch(CoroutineName("Coroutine2")) { 16 | println("[${Thread.currentThread().name}] 코루틴 실행") 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code18/Code7-18.kt: -------------------------------------------------------------------------------- 1 | package section7.code18 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(CoroutineName("Coroutine1")) { 7 | launch(CoroutineName("Coroutine3")) { 8 | println("[${Thread.currentThread().name}] 코루틴 실행") 9 | } 10 | CoroutineScope(Dispatchers.IO).launch(CoroutineName("Coroutine4")) { 11 | println("[${Thread.currentThread().name}] 코루틴 실행") 12 | } 13 | } 14 | 15 | launch(CoroutineName("Coroutine2")) { 16 | println("[${Thread.currentThread().name}] 코루틴 실행") 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code19/Code7-19.kt: -------------------------------------------------------------------------------- 1 | package section7.code19 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(CoroutineName("Coroutine1")) { 7 | launch(CoroutineName("Coroutine3")) { 8 | delay(100L) 9 | println("[${Thread.currentThread().name}] 코루틴 실행 완료") 10 | } 11 | launch(CoroutineName("Coroutine4")) { 12 | delay(100L) 13 | println("[${Thread.currentThread().name}] 코루틴 실행 완료") 14 | } 15 | this.cancel() // Coroutine1의 CoroutineScope에 cancel 요청 16 | } 17 | 18 | launch(CoroutineName("Coroutine2")) { 19 | delay(100L) 20 | println("[${Thread.currentThread().name}] 코루틴 실행 완료") 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/main/kotlin/section7/code2/Code7-2.kt: -------------------------------------------------------------------------------- 1 | package section7.code2 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val coroutineContext = newSingleThreadContext("MyThread") + CoroutineName("CoroutineA") 7 | launch(coroutineContext){ // 부모 코루틴 생성 8 | println("[${Thread.currentThread().name}] 부모 코루틴 실행") 9 | launch { // 자식 코루틴 생성 10 | println("[${Thread.currentThread().name}] 자식 코루틴 실행") 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code20/Code7-20.kt: -------------------------------------------------------------------------------- 1 | package section7.code20 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val whileJob: Job = launch(Dispatchers.Default) { 7 | while(this.isActive) { 8 | println("작업 중") 9 | } 10 | } 11 | delay(100L) 12 | whileJob.cancel() 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code21/Code7-21.kt: -------------------------------------------------------------------------------- 1 | package section7.code21 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { // 루트 코루틴(루트 Job) 생성 6 | println("[${Thread.currentThread().name}] 코루틴 실행") 7 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code22/Code7-22.kt: -------------------------------------------------------------------------------- 1 | package section7.code22 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(CoroutineName("Coroutine1")) { 7 | launch(CoroutineName("Coroutine3")) { 8 | delay(100L) 9 | println("[${Thread.currentThread().name}] 코루틴 실행") 10 | } 11 | launch(CoroutineName("Coroutine4")) { 12 | delay(100L) 13 | println("[${Thread.currentThread().name}] 코루틴 실행") 14 | } 15 | } 16 | launch(CoroutineName("Coroutine2")) { 17 | launch(CoroutineName("Coroutine5")) { 18 | delay(100L) 19 | println("[${Thread.currentThread().name}] 코루틴 실행") 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code23/Code7-23.kt: -------------------------------------------------------------------------------- 1 | package section7.code23 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { // 루트 Job 생성 6 | val newScope = CoroutineScope(Dispatchers.IO) // 새로운 루트 Job 생성 7 | newScope.launch(CoroutineName("Coroutine1")) { 8 | launch(CoroutineName("Coroutine3")) { 9 | delay(100L) 10 | println("[${Thread.currentThread().name}] 코루틴 실행") 11 | } 12 | launch(CoroutineName("Coroutine4")) { 13 | delay(100L) 14 | println("[${Thread.currentThread().name}] 코루틴 실행") 15 | } 16 | } 17 | newScope.launch(CoroutineName("Coroutine2")) { 18 | launch(CoroutineName("Coroutine5")) { 19 | delay(100L) 20 | println("[${Thread.currentThread().name}] 코루틴 실행") 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code24/Code7-24.kt: -------------------------------------------------------------------------------- 1 | package section7.code24 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { // 루트 Job 생성 6 | val newScope = CoroutineScope(Dispatchers.IO) // 새로운 루트 Job 생성 7 | newScope.launch(CoroutineName("Coroutine1")) { 8 | launch(CoroutineName("Coroutine3")) { 9 | delay(100L) 10 | println("[${Thread.currentThread().name}] 코루틴 실행") 11 | } 12 | launch(CoroutineName("Coroutine4")) { 13 | delay(100L) 14 | println("[${Thread.currentThread().name}] 코루틴 실행") 15 | } 16 | } 17 | newScope.launch(CoroutineName("Coroutine2")) { 18 | launch(CoroutineName("Coroutine5")) { 19 | delay(100L) 20 | println("[${Thread.currentThread().name}] 코루틴 실행") 21 | } 22 | } 23 | delay(1000L) 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code25/Code7-25.kt: -------------------------------------------------------------------------------- 1 | package section7.code25 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val newRootJob = Job() // 루트 Job 생성 7 | launch(CoroutineName("Coroutine1") + newRootJob) { 8 | launch(CoroutineName("Coroutine3")) { 9 | delay(100L) 10 | println("[${Thread.currentThread().name}] 코루틴 실행") 11 | } 12 | launch(CoroutineName("Coroutine4")) { 13 | delay(100L) 14 | println("[${Thread.currentThread().name}] 코루틴 실행") 15 | } 16 | } 17 | launch(CoroutineName("Coroutine2") + newRootJob) { 18 | launch(CoroutineName("Coroutine5")) { 19 | delay(100L) 20 | println("[${Thread.currentThread().name}] 코루틴 실행") 21 | } 22 | } 23 | delay(1000L) 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code26/Code7-26.kt: -------------------------------------------------------------------------------- 1 | package section7.code26 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val newRootJob = Job() // 루트 Job 생성 7 | launch(CoroutineName("Coroutine1") + newRootJob) { 8 | launch(CoroutineName("Coroutine3")) { 9 | delay(100L) 10 | println("[${Thread.currentThread().name}] 코루틴 실행") 11 | } 12 | launch(CoroutineName("Coroutine4")) { 13 | delay(100L) 14 | println("[${Thread.currentThread().name}] 코루틴 실행") 15 | } 16 | } 17 | launch(CoroutineName("Coroutine2") + newRootJob) { 18 | launch(CoroutineName("Coroutine5")) { 19 | delay(100L) 20 | println("[${Thread.currentThread().name}] 코루틴 실행") 21 | } 22 | } 23 | newRootJob.cancel() // newRootJob 취소 24 | delay(1000L) 25 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code27/Code7-27.kt: -------------------------------------------------------------------------------- 1 | package section7.code27 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val newRootJob = Job() 7 | launch(CoroutineName("Coroutine1") + newRootJob) { 8 | launch(CoroutineName("Coroutine3")) { 9 | delay(100L) 10 | println("[${Thread.currentThread().name}] 코루틴 실행") 11 | } 12 | launch(CoroutineName("Coroutine4")) { 13 | delay(100L) 14 | println("[${Thread.currentThread().name}] 코루틴 실행") 15 | } 16 | } 17 | launch(CoroutineName("Coroutine2") + newRootJob) { 18 | launch(CoroutineName("Coroutine5") + Job()) { 19 | delay(100L) 20 | println("[${Thread.currentThread().name}] 코루틴 실행") 21 | } 22 | } 23 | delay(50L) // 모든 코루틴이 생성될 때까지 대기 24 | newRootJob.cancel() // 새로운 루트 Job 취소 25 | delay(1000L) 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code28/Code7-28.kt: -------------------------------------------------------------------------------- 1 | package section7.code28 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(CoroutineName("Coroutine1")) { 7 | val newJob = Job() 8 | launch(CoroutineName("Coroutine2") + newJob) { 9 | delay(100L) 10 | println("[${Thread.currentThread().name}] 코루틴 실행") 11 | } 12 | } 13 | delay(1000L) 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code29/Code7-29.kt: -------------------------------------------------------------------------------- 1 | package section7.code29 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(CoroutineName("Coroutine1")) { 7 | val coroutine1Job = this.coroutineContext[Job] // Coroutine1의 Job 8 | val newJob = Job(parent = coroutine1Job) 9 | launch(CoroutineName("Coroutine2") + newJob) { 10 | delay(100L) 11 | println("[${Thread.currentThread().name}] 코루틴 실행") 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code3/Code7-3.kt: -------------------------------------------------------------------------------- 1 | package section7.code3 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val coroutineContext = newSingleThreadContext("MyThread") + CoroutineName("ParentCoroutine") 7 | launch(coroutineContext){ // 부모 코루틴 생성 8 | println("[${Thread.currentThread().name}] 부모 코루틴 실행") 9 | launch(CoroutineName("ChildCoroutine")) { // 자식 코루틴 생성 10 | println("[${Thread.currentThread().name}] 자식 코루틴 실행") 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code30/Code7-30.kt: -------------------------------------------------------------------------------- 1 | package section7.code30 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(CoroutineName("Coroutine1")) { 7 | val coroutine1Job = this.coroutineContext[Job] 8 | val newJob = Job(coroutine1Job) 9 | launch(CoroutineName("Coroutine2") + newJob) { 10 | delay(100L) 11 | println("[${Thread.currentThread().name}] 코루틴 실행") 12 | } 13 | newJob.complete() // 명시적으로 완료 호출 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code31/Code7-31.kt: -------------------------------------------------------------------------------- 1 | package section7.code31 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | delay(5000L) 7 | println("[${Thread.currentThread().name}] 코루틴 종료") 8 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code32/Code7-32.kt: -------------------------------------------------------------------------------- 1 | package section7.code32 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch { 7 | delay(1000L) 8 | println("[${Thread.currentThread().name}] launch 코루틴 종료") 9 | } 10 | delay(2000L) 11 | println("[${Thread.currentThread().name}] runBlocking 코루틴 종료") 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/main/kotlin/section7/code4/Code7-4.kt: -------------------------------------------------------------------------------- 1 | package section7.code4 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { // 부모 코루틴 생성 6 | val runBlockingJob = coroutineContext[Job] // 부모 코루틴의 Job 추출 7 | launch { // 자식 코루틴 생성 8 | val launchJob = coroutineContext[Job] // 자식 코루틴의 Job 추출 9 | if (runBlockingJob === launchJob) { 10 | println("runBlocking으로 생성된 Job과 launch로 생성된 Job이 동일합니다") 11 | } else { 12 | println("runBlocking으로 생성된 Job과 launch로 생성된 Job이 다릅니다") 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code5/Code7-5.kt: -------------------------------------------------------------------------------- 1 | package section7.code5 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val runBlockingJob = coroutineContext[Job] 7 | println(runBlockingJob?.parent) 8 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code6/Code7-6.kt: -------------------------------------------------------------------------------- 1 | package section7.code6 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { // 부모 코루틴 6 | val parentJob = coroutineContext[Job] // 부모 코루틴의 CoroutineContext로부터 부모 코루틴의 Job 추출 7 | launch { // 자식 코루틴 8 | val childJob = coroutineContext[Job] // 자식 코루틴의 CoroutineContext로부터 자식 코루틴의 Job 추출 9 | println("1. 자식 코루틴의 Job이 가지고 있는 parent는 부모 코루틴의 Job인가? ${childJob?.parent === parentJob}") 10 | println("2. 부모 코루틴의 Job은 자식 코루틴의 Job을 참조를 가지는가? ${parentJob?.children?.contains(childJob)}") 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code7/Code7-7.kt: -------------------------------------------------------------------------------- 1 | package section7.code7 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val parentJob = launch(Dispatchers.IO) { // 부모 코루틴 생성 7 | val dbResultsDeferred: List> = listOf("db1", "db2", "db3").map { 8 | async { // 자식 코루틴 생성 9 | delay(1000L) // DB로부터 데이터를 가져오는데 걸리는 시간 10 | println("${it}로부터 데이터를 가져오는데 성공했습니다") 11 | return@async "[${it}]data" 12 | } 13 | } 14 | val dbResults: List = dbResultsDeferred.awaitAll() // 모든 코루틴이 완료될 때까지 대기 15 | 16 | println(dbResults) // 화면에 표시 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code8/Code7-8.kt: -------------------------------------------------------------------------------- 1 | package section7.code8 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val parentJob = launch(Dispatchers.IO){ // 부모 코루틴 생성 7 | val dbResultsDeferred: List> = listOf("db1","db2","db3").map { 8 | async { // 자식 코루틴 생성 9 | delay(1000L) // DB로부터 데이터를 가져오는데 걸리는 시간 10 | println("${it}로부터 데이터를 가져오는데 성공했습니다") 11 | return@async "[${it}]data" 12 | } 13 | } 14 | val dbResults: List = dbResultsDeferred.awaitAll() // 모든 코루틴이 완료될 때까지 대기 15 | 16 | println(dbResults) // 화면에 표시 17 | } 18 | parentJob.cancel() // 부모 코루틴에 취소 요청 19 | } -------------------------------------------------------------------------------- /src/main/kotlin/section7/code9/Code7-9.kt: -------------------------------------------------------------------------------- 1 | package section7.code9 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | val parentJob = launch { // 부모 코루틴 실행 8 | launch { // 자식 코루틴 실행 9 | delay(1000L) // 1초간 대기 10 | println("[${getElapsedTime(startTime)}] 자식 코루틴 실행 완료") 11 | } 12 | println("[${getElapsedTime(startTime)}] 부모 코루틴이 실행하는 마지막 코드") 13 | } 14 | parentJob.invokeOnCompletion { // 부모 코루틴이 완료될 시 호출되는 콜백 등록 15 | println("[${getElapsedTime(startTime)}] 부모 코루틴 실행 완료") 16 | } 17 | } 18 | 19 | private fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" -------------------------------------------------------------------------------- /src/main/kotlin/section8/code1/Code8-1.kt: -------------------------------------------------------------------------------- 1 | package section8.code1 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(CoroutineName("Coroutine1")) { 7 | launch(CoroutineName("Coroutine3")) { 8 | throw Exception("예외 발생") 9 | } 10 | delay(100L) 11 | println("[${Thread.currentThread().name}] 코루틴 실행") 12 | } 13 | launch(CoroutineName("Coroutine2")) { 14 | delay(100L) 15 | println("[${Thread.currentThread().name}] 코루틴 실행") 16 | } 17 | delay(1000L) 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/section8/code10/Code8-10.kt: -------------------------------------------------------------------------------- 1 | package section8.code10 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> 7 | println("[예외 발생] ${throwable}") 8 | } 9 | CoroutineScope(Dispatchers.IO).launch(CoroutineName("Coroutine1")) { 10 | launch(CoroutineName("Coroutine2") + exceptionHandler) { 11 | throw Exception("Coroutine2에 예외가 발생했습니다") 12 | } 13 | } 14 | delay(1000L) 15 | } 16 | -------------------------------------------------------------------------------- /src/main/kotlin/section8/code11/Code8-11.kt: -------------------------------------------------------------------------------- 1 | package section8.code11 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> 7 | println("[예외 발생] ${throwable}") 8 | } 9 | val exceptionHandler2 = CoroutineExceptionHandler { coroutineContext, throwable -> 10 | println("[예외 발생2] ${throwable}") 11 | } 12 | 13 | CoroutineScope(exceptionHandler) 14 | .launch(CoroutineName("Coroutine1") + exceptionHandler2) { 15 | launch(CoroutineName("Coroutine2")) { 16 | throw Exception("Coroutine2에 예외가 발생했습니다") 17 | } 18 | } 19 | delay(1000L) 20 | } 21 | -------------------------------------------------------------------------------- /src/main/kotlin/section8/code12/Code8-12.kt: -------------------------------------------------------------------------------- 1 | package section8.code12 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> 7 | println("[예외 로깅] ${throwable}") 8 | } 9 | 10 | CoroutineScope(Dispatchers.IO) 11 | .launch(CoroutineName("Coroutine1") + exceptionHandler) { 12 | launch(CoroutineName("Coroutine2")) { 13 | throw Exception("Coroutine2에 예외가 발생했습니다") 14 | } 15 | launch(CoroutineName("Coroutine3")) { 16 | // 다른 작업 17 | } 18 | } 19 | 20 | delay(1000L) 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/section8/code13/Code8-13.kt: -------------------------------------------------------------------------------- 1 | package section8.code13 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(CoroutineName("Coroutine1")) { 7 | try { 8 | throw Exception("Coroutine1에 예외가 발생했습니다") 9 | } catch (e: Exception) { 10 | println(e.message) 11 | } 12 | } 13 | launch(CoroutineName("Coroutine2")) { 14 | delay(100L) 15 | println("Coroutine2 실행 완료") 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/section8/code14/Code8-14.kt: -------------------------------------------------------------------------------- 1 | package section8.code14 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | try { 7 | launch(CoroutineName("Coroutine1")) { 8 | throw Exception("Coroutine1에 예외가 발생했습니다") 9 | }.join() 10 | } catch (e: Exception) { 11 | println(e.message) 12 | } 13 | launch(CoroutineName("Coroutine2")) { 14 | delay(100L) 15 | println("Coroutine2 실행 완료") 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/section8/code15/Code8-15.kt: -------------------------------------------------------------------------------- 1 | package section8.code15 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | supervisorScope { 7 | val deferred: Deferred = async(CoroutineName("Coroutine1")) { 8 | throw Exception("Coroutine1에 예외가 발생했습니다") 9 | } 10 | try { 11 | deferred.await() 12 | } catch (e: Exception) { 13 | println("[노출된 예외] ${e.message}") 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/section8/code16/Code8-16.kt: -------------------------------------------------------------------------------- 1 | package section8.code16 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | async(CoroutineName("Coroutine1")) { 7 | throw Exception("Coroutine1에 예외가 발생했습니다") 8 | } 9 | launch(CoroutineName("Coroutine2")) { 10 | delay(100L) 11 | println("[${Thread.currentThread().name}] 코루틴 실행") 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section8/code17/Code8-17.kt: -------------------------------------------------------------------------------- 1 | package section8.code17 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | supervisorScope { 7 | async(CoroutineName("Coroutine1")) { 8 | throw Exception("Coroutine1에 예외가 발생했습니다") 9 | } 10 | launch(CoroutineName("Coroutine2")) { 11 | delay(100L) 12 | println("[${Thread.currentThread().name}] 코루틴 실행") 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/section8/code18/Code8-18.kt: -------------------------------------------------------------------------------- 1 | package section8.code18 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking(CoroutineName("runBlocking 코루틴")) { 6 | launch(CoroutineName("Coroutine1")) { 7 | launch(CoroutineName("Coroutine2")) { 8 | throw CancellationException() 9 | } 10 | delay(100L) 11 | println("[${Thread.currentThread().name}] 코루틴 실행") 12 | } 13 | delay(100L) 14 | println("[${Thread.currentThread().name}] 코루틴 실행") 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/section8/code19/Code8-19.kt: -------------------------------------------------------------------------------- 1 | package section8.code19 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val job = launch { 7 | delay(1000L) // 1초간 지속 8 | } 9 | job.invokeOnCompletion { exception -> 10 | println(exception) // 발생한 예외 출력 11 | } 12 | job.cancel() // job 취소 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/section8/code2/Code8-2.kt: -------------------------------------------------------------------------------- 1 | package section8.code2 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(CoroutineName("Parent Coroutine")) { 7 | // 새로운 Job 객체를 만들어 Coroutine1에 연결 8 | launch(CoroutineName("Coroutine1") + Job()) { 9 | launch(CoroutineName("Coroutine3")) { 10 | throw Exception("예외 발생") 11 | } 12 | delay(100L) 13 | println("[${Thread.currentThread().name}] 코루틴 실행") 14 | } 15 | launch(CoroutineName("Coroutine2")) { 16 | delay(100L) 17 | println("[${Thread.currentThread().name}] 코루틴 실행") 18 | } 19 | } 20 | delay(1000L) 21 | } -------------------------------------------------------------------------------- /src/main/kotlin/section8/code3/Code8-3.kt: -------------------------------------------------------------------------------- 1 | package section8.code3 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val parentJob = launch(CoroutineName("Parent Coroutine")) { 7 | // 새로운 Job 객체를 만들어 Coroutine1에 연결 8 | launch(CoroutineName("Coroutine1") + Job()) { 9 | launch(CoroutineName("Coroutine3")) { 10 | delay(100L) 11 | println("[${Thread.currentThread().name}] 코루틴 실행") 12 | } 13 | delay(100L) 14 | println("[${Thread.currentThread().name}] 코루틴 실행") 15 | } 16 | launch(CoroutineName("Coroutine2")) { 17 | delay(100L) 18 | println("[${Thread.currentThread().name}] 코루틴 실행") 19 | } 20 | } 21 | delay(20L) // 코루틴들이 모두 생성될 때까지 대기 22 | parentJob.cancel() // Parent Coroutine에 취소 요청 23 | delay(1000L) 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/section8/code4/Code8-4.kt: -------------------------------------------------------------------------------- 1 | package section8.code4 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val supervisorJob = SupervisorJob() 7 | launch(CoroutineName("Coroutine1") + supervisorJob) { 8 | launch(CoroutineName("Coroutine3")) { 9 | throw Exception("예외 발생") 10 | } 11 | delay(100L) 12 | println("[${Thread.currentThread().name}] 코루틴 실행") 13 | } 14 | launch(CoroutineName("Coroutine2") + supervisorJob) { 15 | delay(100L) 16 | println("[${Thread.currentThread().name}] 코루틴 실행") 17 | } 18 | delay(1000L) 19 | } -------------------------------------------------------------------------------- /src/main/kotlin/section8/code5/Code8-5.kt: -------------------------------------------------------------------------------- 1 | package section8.code5 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | // supervisorJob의 부모로 runBlocking으로 생성된 Job 객체 설정 7 | val supervisorJob = SupervisorJob(parent = this.coroutineContext[Job]) 8 | launch(CoroutineName("Coroutine1") + supervisorJob) { 9 | launch(CoroutineName("Coroutine3")) { 10 | throw Exception("예외 발생") 11 | } 12 | delay(100L) 13 | println("[${Thread.currentThread().name}] 코루틴 실행") 14 | } 15 | launch(CoroutineName("Coroutine2") + supervisorJob) { 16 | delay(100L) 17 | println("[${Thread.currentThread().name}] 코루틴 실행") 18 | } 19 | supervisorJob.complete() // supervisorJob 완료 처리 20 | } 21 | -------------------------------------------------------------------------------- /src/main/kotlin/section8/code6/Code8-6.kt: -------------------------------------------------------------------------------- 1 | package section8.code6 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val coroutineScope = CoroutineScope(SupervisorJob()) 7 | coroutineScope.apply { 8 | launch(CoroutineName("Coroutine1")) { 9 | launch(CoroutineName("Coroutine3")) { 10 | throw Exception("예외 발생") 11 | } 12 | delay(100L) 13 | println("[${Thread.currentThread().name}] 코루틴 실행") 14 | } 15 | launch(CoroutineName("Coroutine2")) { 16 | delay(100L) 17 | println("[${Thread.currentThread().name}] 코루틴 실행") 18 | } 19 | } 20 | delay(1000L) 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/section8/code7/Code8-7.kt: -------------------------------------------------------------------------------- 1 | package section8.code7 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | launch(CoroutineName("Parent Coroutine") + SupervisorJob()) { 7 | launch(CoroutineName("Coroutine1")) { 8 | launch(CoroutineName("Coroutine3")) { 9 | throw Exception("예외 발생") 10 | } 11 | delay(100L) 12 | println("[${Thread.currentThread().name}] 코루틴 실행") 13 | } 14 | launch(CoroutineName("Coroutine2")) { 15 | delay(100L) 16 | println("[${Thread.currentThread().name}] 코루틴 실행") 17 | } 18 | } 19 | delay(1000L) 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/section8/code8/Code8-8.kt: -------------------------------------------------------------------------------- 1 | package section8.code8 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | supervisorScope { 7 | launch(CoroutineName("Coroutine1")) { 8 | launch(CoroutineName("Coroutine3")) { 9 | throw Exception("예외 발생") 10 | } 11 | delay(100L) 12 | println("[${Thread.currentThread().name}] 코루틴 실행") 13 | } 14 | launch(CoroutineName("Coroutine2")) { 15 | delay(100L) 16 | println("[${Thread.currentThread().name}] 코루틴 실행") 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/kotlin/section8/code9/Code8-9.kt: -------------------------------------------------------------------------------- 1 | package section8.code9 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> 7 | println("[예외 발생] ${throwable}") 8 | } 9 | CoroutineScope(context = exceptionHandler).launch(CoroutineName("Coroutine1")) { 10 | launch(CoroutineName("Coroutine2")) { 11 | throw Exception("Coroutine2에 예외가 발생했습니다") 12 | } 13 | } 14 | delay(1000L) 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/section9/code1/Code9-1.kt: -------------------------------------------------------------------------------- 1 | package section9.code1 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | delay(1000L) 7 | println("Hello World") 8 | delay(1000L) 9 | println("Hello World") 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/section9/code10/Code9-10.kt: -------------------------------------------------------------------------------- 1 | package section9.code10 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | val results = searchByKeyword("Keyword") // 검색 실행 및 결과 값 반환 받기 8 | println("[결과] ${results.toList()}") // 결과값 출력 9 | println(getElapsedTime(startTime)) 10 | } 11 | 12 | suspend fun searchByKeyword(keyword: String): Array = coroutineScope { 13 | val dbResultsDeferred = async { 14 | searchFromDB(keyword) 15 | } 16 | val serverResultsDeferred = async { 17 | searchFromServer(keyword) 18 | } 19 | 20 | return@coroutineScope arrayOf(*dbResultsDeferred.await(), *serverResultsDeferred.await()) 21 | } 22 | 23 | suspend fun searchFromDB(keyword: String): Array { 24 | delay(1000L) 25 | return arrayOf("[DB]${keyword}1", "[DB]${keyword}2") 26 | } 27 | 28 | suspend fun searchFromServer(keyword: String): Array { 29 | delay(1000L) 30 | return arrayOf("[Server]${keyword}1", "[Server]${keyword}2") 31 | } 32 | 33 | fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" 34 | -------------------------------------------------------------------------------- /src/main/kotlin/section9/code11/Code9-11.kt: -------------------------------------------------------------------------------- 1 | package section9.code11 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | val results = searchByKeyword("Keyword") // 검색 실행 및 결과 값 반환 받기 8 | println("[결과] ${results.toList()}") // 결과값 출력 9 | println(getElapsedTime(startTime)) 10 | } 11 | 12 | suspend fun searchByKeyword(keyword: String): Array = coroutineScope { 13 | val dbResultsDeferred = async { 14 | throw Exception("DB 읽기 오류 발생") 15 | searchFromDB(keyword) 16 | } 17 | val serverResultsDeferred = async { 18 | searchFromServer(keyword) 19 | } 20 | 21 | return@coroutineScope arrayOf(*dbResultsDeferred.await(), *serverResultsDeferred.await()) 22 | } 23 | 24 | suspend fun searchFromDB(keyword: String): Array { 25 | delay(1000L) 26 | return arrayOf("[DB]${keyword}1", "[DB]${keyword}2") 27 | } 28 | 29 | suspend fun searchFromServer(keyword: String): Array { 30 | delay(1000L) 31 | return arrayOf("[Server]${keyword}1", "[Server]${keyword}2") 32 | } 33 | 34 | fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" 35 | -------------------------------------------------------------------------------- /src/main/kotlin/section9/code12/Code9-12.kt: -------------------------------------------------------------------------------- 1 | package section9.code12 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | val results = searchByKeyword("Keyword") // 검색 실행 및 결과 값 반환 받기 8 | println("[결과] ${results.toList()}") // 결과값 출력 9 | println(section9.code11.getElapsedTime(startTime)) 10 | } 11 | 12 | suspend fun searchByKeyword(keyword: String): Array = supervisorScope { 13 | val dbResultsDeferred = async { 14 | throw Exception("DB 읽기 오류 발생") 15 | searchFromDB(keyword) 16 | } 17 | val serverResultsDeferred = async { 18 | searchFromServer(keyword) 19 | } 20 | 21 | val dbResults = try { 22 | dbResultsDeferred.await() 23 | } catch (e: Exception) { 24 | arrayOf() // 예외 발생 시 빈 결과 반환 25 | } 26 | 27 | val serverResults = try { 28 | serverResultsDeferred.await() 29 | } catch (e: Exception) { 30 | arrayOf() // 예외 발생 시 빈 결과 반환 31 | } 32 | 33 | return@supervisorScope arrayOf(*dbResults, *serverResults) 34 | } 35 | 36 | suspend fun searchFromDB(keyword: String): Array { 37 | delay(1000L) 38 | return arrayOf("[DB]${keyword}1", "[DB]${keyword}2") 39 | } 40 | 41 | suspend fun searchFromServer(keyword: String): Array { 42 | delay(1000L) 43 | return arrayOf("[Server]${keyword}1", "[Server]${keyword}2") 44 | } 45 | 46 | fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" -------------------------------------------------------------------------------- /src/main/kotlin/section9/code2/Code9-2.kt: -------------------------------------------------------------------------------- 1 | package section9.code2 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | delayAndPrintHelloWorld() 7 | delayAndPrintHelloWorld() 8 | } 9 | 10 | suspend fun delayAndPrintHelloWorld() { 11 | delay(1000L) 12 | println("Hello World") 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/main/kotlin/section9/code3/Code9-3.kt: -------------------------------------------------------------------------------- 1 | package section9.code3 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | delayAndPrintHelloWorld() 8 | delayAndPrintHelloWorld() 9 | println(getElapsedTime(startTime)) 10 | } 11 | 12 | suspend fun delayAndPrintHelloWorld() { 13 | delay(1000L) 14 | println("Hello World") 15 | } 16 | 17 | fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" -------------------------------------------------------------------------------- /src/main/kotlin/section9/code4/Code9-4.kt: -------------------------------------------------------------------------------- 1 | package section9.code4 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | delay(1000L) 8 | println("Hello World") 9 | delay(1000L) 10 | println("Hello World") 11 | println(getElapsedTime(startTime)) 12 | } 13 | 14 | fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" -------------------------------------------------------------------------------- /src/main/kotlin/section9/code5/Code9-5.kt: -------------------------------------------------------------------------------- 1 | package section9.code5 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | val job1 = launch { 8 | delayAndPrintHelloWorld() 9 | } 10 | val job2 = launch { 11 | delayAndPrintHelloWorld() 12 | } 13 | joinAll(job1, job2) 14 | println(getElapsedTime(startTime)) 15 | } 16 | 17 | suspend fun delayAndPrintHelloWorld() { 18 | delay(1000L) 19 | println("Hello World") 20 | } 21 | 22 | private fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" -------------------------------------------------------------------------------- /src/main/kotlin/section9/code6/Code9-6.kt: -------------------------------------------------------------------------------- 1 | package section9.code6 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val startTime = System.currentTimeMillis() 7 | val job1 = launch { 8 | delay(1000L) 9 | println("Hello World") 10 | } 11 | val job2 = launch { 12 | delay(1000L) 13 | println("Hello World") 14 | } 15 | joinAll(job1, job2) 16 | println(getElapsedTime(startTime)) 17 | } 18 | 19 | fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" -------------------------------------------------------------------------------- /src/main/kotlin/section9/code7/Code9-7.kt: -------------------------------------------------------------------------------- 1 | package section9.code7 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | delayAndPrint(keyword = "부모 코루틴이 실행 중입니다") 7 | launch { 8 | delayAndPrint(keyword = "자식 코루틴이 실행 중입니다") 9 | } 10 | } 11 | 12 | suspend fun delayAndPrint(keyword: String) { 13 | delay(1000L) 14 | println(keyword) 15 | } 16 | -------------------------------------------------------------------------------- /src/main/kotlin/section9/code8/Code9-8.kt: -------------------------------------------------------------------------------- 1 | package section9.code8 2 | 3 | import kotlinx.coroutines.* 4 | 5 | suspend fun searchByKeyword(keyword: String): Array { 6 | val dbResults = searchFromDB(keyword) 7 | val serverResults = searchFromServer(keyword) 8 | return arrayOf(*dbResults, *serverResults) 9 | } 10 | 11 | suspend fun searchFromDB(keyword: String): Array { 12 | delay(1000L) 13 | return arrayOf("[DB]${keyword}1", "[DB]${keyword}2") 14 | } 15 | 16 | suspend fun searchFromServer(keyword: String): Array { 17 | delay(1000L) 18 | return arrayOf("[Server]${keyword}1", "[Server]${keyword}2") 19 | } -------------------------------------------------------------------------------- /src/main/kotlin/section9/code9/Code9-9.kt: -------------------------------------------------------------------------------- 1 | package section9.code9 2 | 3 | import kotlinx.coroutines.* 4 | 5 | // 주석을 지우고 오류 확인 6 | //suspend fun searchByKeyword(keyword: String): Array { 7 | // val dbResultsDeferred = async { 8 | // searchFromDB(keyword) 9 | // } 10 | // val serverResultsDeferred = async { 11 | // searchFromServer(keyword) 12 | // } 13 | // return arrayOf(*dbResultsDeferred.await(), *serverResultsDeferred.await()) 14 | //} 15 | 16 | suspend fun searchFromDB(keyword: String): Array { 17 | delay(1000L) 18 | return arrayOf("[DB]${keyword}1", "[DB]${keyword}2") 19 | } 20 | 21 | suspend fun searchFromServer(keyword: String): Array { 22 | delay(1000L) 23 | return arrayOf("[Server]${keyword}1", "[Server]${keyword}2") 24 | } -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code1/AddUseCaseTest.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code1 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | import section12.code1.AddUseCase 6 | 7 | class AddUseCaseTest { 8 | @Test 9 | fun `1,2,3을 더하면 6이다`() { 10 | val addUseCase: AddUseCase = AddUseCase() 11 | val result = addUseCase.add(1, 2, 3) 12 | assertEquals(6, result) 13 | } 14 | 15 | @Test 16 | fun `실패하는 테스트_1,2,3을 더하면 5이다`() { 17 | val addUseCase: AddUseCase = AddUseCase() 18 | val result = addUseCase.add(1, 2, 3) 19 | assertEquals(5, result) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code1/AddUseCaseTestWithBeforeEach.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code1 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.BeforeEach 5 | import org.junit.jupiter.api.Test 6 | import section12.code1.AddUseCase 7 | 8 | class AddUseCaseTestWithBeforeEach { 9 | private lateinit var addUseCase: AddUseCase 10 | 11 | @BeforeEach 12 | fun setUp() { 13 | addUseCase = AddUseCase() 14 | } 15 | 16 | @Test 17 | fun `1,2,3을 더하면 6이다`() { 18 | val result = addUseCase.add(1, 2, 3) 19 | assertEquals(6, result) 20 | } 21 | 22 | @Test 23 | fun `-1더하기 1은 0이다`() { 24 | val result = addUseCase.add(-1, 1) 25 | assertEquals(0, result) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code2/FakeUserPhoneNumberRepository.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code2 2 | 3 | import section12.code2.UserPhoneNumberRepository 4 | 5 | class FakeUserPhoneNumberRepository : UserPhoneNumberRepository { 6 | private val userPhoneNumberMap = mutableMapOf() 7 | 8 | override fun saveUserPhoneNumber(id: String, phoneNumber: String) { 9 | userPhoneNumberMap[id] = phoneNumber 10 | } 11 | 12 | override fun getPhoneNumberByUserId(id: String): String { 13 | return userPhoneNumberMap[id] ?: "" 14 | } 15 | } -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code2/StubUserNameRepository.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code2 2 | 3 | import section12.code2.UserNameRepository 4 | 5 | // 유연하지 않은 StubUserNameRepository 6 | //class StubUserNameRepository : UserNameRepository { 7 | // private val userNameMap = mapOf( 8 | // "0x1111" to "홍길동", 9 | // "0x2222" to "조세영" 10 | // ) 11 | // 12 | // override fun saveUserName(id: String, name: String) { 13 | // // 구현하지 않는다. 14 | // } 15 | // 16 | // override fun getNameByUserId(id: String): String { 17 | // return userNameMap[id] ?: "" 18 | // } 19 | //} 20 | 21 | // 유연한 StubUserNameRepository 22 | 23 | class StubUserNameRepository( 24 | private val userNameMap: Map 25 | ) : UserNameRepository { 26 | override fun saveUserName(id: String, name: String) { 27 | // 구현하지 않는다. 28 | } 29 | 30 | override fun getNameByUserId(id: String): String { 31 | return userNameMap[id] ?: "" 32 | } 33 | } -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code2/UserProfileFetcherTest.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code2 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | import section12.code2.UserProfileFetcher 6 | 7 | class UserProfileFetcherTest { 8 | @Test 9 | fun `UserNameRepository가 적절한 유저 프로필을 반환한다`() { 10 | // Given 11 | val userProfileFetcher = UserProfileFetcher( 12 | userNameRepository = StubUserNameRepository( 13 | userNameMap = mapOf( 14 | "0x1111" to "홍길동", 15 | "0x2222" to "조세영" 16 | ) 17 | ), 18 | userPhoneNumberRepository = FakeUserPhoneNumberRepository().apply { 19 | this.saveUserPhoneNumber("0x1111", "010-xxxx-xxxx") 20 | } 21 | ) 22 | 23 | // When 24 | val userProfile = userProfileFetcher.getUserProfileById("0x1111") 25 | 26 | // Then 27 | assertEquals("홍길동", userProfile.name) 28 | assertEquals("010-xxxx-xxxx", userProfile.phoneNumber) 29 | } 30 | } -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code3/RepeatAddUseCaseTest.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code3 2 | 3 | import kotlinx.coroutines.runBlocking 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | import org.junit.jupiter.api.Test 6 | import section12.code3.RepeatAddUseCase 7 | 8 | /** 9 | * 오류가 생기는 테스트 10 | * 일시 중단 함수로 인해 아래와 같이 작성하면 오류가 생긴다. 11 | */ 12 | //class RepeatAddUseCaseTest { 13 | // @Test 14 | // fun `100번 더하면 100이 반환된다`() { 15 | // // Given 16 | // val repeatAddUseCase = RepeatAddUseCase() 17 | // 18 | // // When 19 | // val result = repeatAddUseCase.add(100) 20 | // 21 | // // Then 22 | // assertEquals(100, result) 23 | // } 24 | //} 25 | 26 | 27 | class RepeatAddUseCaseTest { 28 | @Test 29 | fun `100번 더하면 100이 반환된다`() = runBlocking { 30 | // Given 31 | val repeatAddUseCase = RepeatAddUseCase() 32 | 33 | // When 34 | val result = repeatAddUseCase.add(100) 35 | 36 | // Then 37 | assertEquals(100, result) 38 | } 39 | } -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code4/RepeatAddWithDelayUseCaseTest.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code4 2 | 3 | import kotlinx.coroutines.runBlocking 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | import org.junit.jupiter.api.Test 6 | import section12.code4.RepeatAddWithDelayUseCase 7 | 8 | class RepeatAddWithDelayUseCaseTest { 9 | @Test 10 | fun `runBlocking_100번 더하면 100이 반환된다`() = runBlocking { 11 | // Given 12 | val repeatAddUseCase = RepeatAddWithDelayUseCase() 13 | 14 | // When 15 | val result = repeatAddUseCase.add(repeatTime = 100) 16 | 17 | // Then 18 | assertEquals(100, result) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code5/TestCoroutineScheduler.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code5 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.launch 7 | import kotlinx.coroutines.test.* 8 | import kotlinx.coroutines.test.TestCoroutineScheduler 9 | import org.junit.jupiter.api.Assertions 10 | import org.junit.jupiter.api.Assertions.assertEquals 11 | import org.junit.jupiter.api.Test 12 | 13 | @OptIn(ExperimentalCoroutinesApi::class) 14 | class TestCoroutineScheduler { 15 | @Test 16 | fun `가상 시간 조절 테스트`() { 17 | val testCoroutineScheduler = TestCoroutineScheduler() 18 | 19 | testCoroutineScheduler.advanceTimeBy(5000L) // 5초를 흐르게 만듦 20 | assertEquals(5000L, testCoroutineScheduler.currentTime) // 현재 시간이 5초임을 단언 21 | testCoroutineScheduler.advanceTimeBy(6000L) // 6초를 흐르게 만듦 22 | assertEquals(11000L, testCoroutineScheduler.currentTime) // 현재 시간이 11초임을 단언 23 | testCoroutineScheduler.advanceTimeBy(10000L) // 10초를 흐르게 만듦 24 | assertEquals(21000L, testCoroutineScheduler.currentTime) // 현재 시간이 21초임을 단언 25 | } 26 | 27 | @Test 28 | fun `가상 시간 위에서 테스트 진행`() { 29 | // Given 30 | val testCoroutineScheduler: TestCoroutineScheduler = TestCoroutineScheduler() 31 | val testDispatcher: TestDispatcher = StandardTestDispatcher(scheduler = testCoroutineScheduler) 32 | val testCoroutineScope = CoroutineScope(context = testDispatcher) 33 | 34 | var result = 0 35 | 36 | // When 37 | testCoroutineScope.launch { 38 | delay(10000L) // 10초간 대기 39 | result = 1 40 | delay(10000L) // 10초간 대기 41 | result = 2 42 | } 43 | 44 | // Then 45 | testCoroutineScheduler.advanceTimeBy(5000L) // 5초를 흐르게 만듦 46 | assertEquals(0, result) 47 | testCoroutineScheduler.advanceTimeBy(6000L) // 6초를 흐르게 만듦 48 | assertEquals(1, result) 49 | testCoroutineScheduler.advanceTimeBy(10000L) // 10초를 흐르게 만듦 50 | assertEquals(2, result) 51 | } 52 | 53 | @Test 54 | fun `advanceUntilIdle의 동작 살펴보기`() { 55 | // Given 56 | val testCoroutineScheduler: TestCoroutineScheduler = TestCoroutineScheduler() 57 | val testDispatcher: TestDispatcher = StandardTestDispatcher(scheduler = testCoroutineScheduler) 58 | val testCoroutineScope = CoroutineScope(context = testDispatcher) 59 | 60 | var result = 0 61 | 62 | // When 63 | testCoroutineScope.launch { 64 | delay(10_000L) // 10초간 대기 65 | result = 1 66 | delay(10_000L) // 10초간 대기 67 | result = 2 68 | } 69 | testCoroutineScheduler.advanceUntilIdle() // testCoroutineScope 하위의 코루틴이 모두 실행되게 만듦 70 | 71 | // Then 72 | assertEquals(2, result) 73 | } 74 | 75 | @Test 76 | fun `StandardTestDispatcher 사용하기`() { 77 | // 테스트 환경 설정 78 | val testDispatcher: TestDispatcher = StandardTestDispatcher() 79 | val testCoroutineScope = CoroutineScope(context = testDispatcher) 80 | 81 | var result = 0 82 | 83 | // When 84 | testCoroutineScope.launch { 85 | delay(10_000L) // 10초간 대기 86 | result = 1 87 | delay(10_000L) // 10초간 대기 88 | result = 2 89 | } 90 | testDispatcher.scheduler.advanceUntilIdle() 91 | 92 | // Then 93 | assertEquals(2, result) 94 | } 95 | 96 | @Test 97 | fun `TestScope 사용하기`() { 98 | // Given 99 | val testCoroutineScope: TestScope = TestScope() 100 | 101 | var result = 0 102 | 103 | // When 104 | testCoroutineScope.launch { 105 | delay(10000L) // 10초간 대기 106 | result = 1 107 | delay(10000L) // 10초간 대기 108 | result = 2 109 | } 110 | testCoroutineScope.advanceUntilIdle() 111 | 112 | // Then 113 | assertEquals(2, result) 114 | } 115 | 116 | @Test 117 | fun `runTest 사용하기`() { 118 | // Given 119 | var result = 0 120 | 121 | // When 122 | runTest { 123 | delay(10000L) // 10초간 대기 124 | result = 1 125 | delay(10000L) // 10초간 대기 126 | result = 2 127 | } 128 | 129 | // Then 130 | assertEquals(2, result) 131 | } 132 | 133 | @Test 134 | fun `runTest로 테스트 감싸기`() = runTest { 135 | // Given 136 | var result = 0 137 | 138 | // When 139 | delay(10000L) // 10초간 대기 140 | result = 1 141 | delay(10000L) // 10초간 대기 142 | result = 2 143 | 144 | // Then 145 | assertEquals(2, result) 146 | } 147 | 148 | @Test 149 | fun `runTest 내부에서 advanceUntilIdle 사용하기`() = runTest { 150 | var result = 0 151 | launch { 152 | delay(1000L) 153 | result = 1 154 | } 155 | 156 | println("가상 시간: ${this.currentTime}ms, result = ${result}") 157 | advanceUntilIdle() 158 | println("가상 시간: ${this.currentTime}ms, result = ${result}") 159 | } 160 | } -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code6/FollowerSearcherTest.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code6 2 | 3 | import kotlinx.coroutines.test.runTest 4 | import org.junit.jupiter.api.Assertions 5 | import org.junit.jupiter.api.BeforeEach 6 | import org.junit.jupiter.api.Test 7 | import section12.code6.Follower 8 | import section12.code6.FollowerSearcher 9 | 10 | class FollowerSearcherTest { 11 | private lateinit var followerSearcher: FollowerSearcher 12 | 13 | @BeforeEach 14 | fun setUp() { 15 | followerSearcher = FollowerSearcher( 16 | officialAccountRepository = stubOfficialAccountRepository, 17 | personAccountRepository = stubPersonAccountRepository 18 | ) 19 | } 20 | 21 | @Test 22 | fun `공식 계정과 개인 계정이 합쳐져 반환되는지 테스트`() = runTest { 23 | // Given 24 | val searchName = "A" 25 | val expectedResults = listOf(companyA, personA) 26 | 27 | // When 28 | val results = followerSearcher.searchByName(searchName) 29 | 30 | // Then 31 | Assertions.assertEquals(expectedResults, results) 32 | } 33 | 34 | @Test 35 | fun `빈 배열이 반환되는지 테스트`() = runTest { 36 | // Given 37 | val searchName = "Empty" 38 | val expectedResults = emptyList() 39 | 40 | // When 41 | val results = followerSearcher.searchByName(searchName) 42 | 43 | // Then 44 | Assertions.assertEquals(expectedResults, results) 45 | } 46 | 47 | companion object { 48 | private val companyA = Follower.OfficialAccount(id = "0x0000", name = "CompanyA") 49 | private val companyB = Follower.OfficialAccount(id = "0x0001", name = "CompanyB") 50 | private val companyC = Follower.OfficialAccount(id = "0x0002", name = "CompanyC") 51 | 52 | private val stubOfficialAccountRepository = StubOfficialAccountRepository( 53 | users = listOf(companyA, companyB, companyC) 54 | ) 55 | 56 | private val personA = Follower.PersonAccount(id = "0x1000", name = "PersonA") 57 | private val personB = Follower.PersonAccount(id = "0x1001", name = "PersonB") 58 | private val personC = Follower.PersonAccount(id = "0x1002", name = "PersonC") 59 | 60 | private val stubPersonAccountRepository = StubPersonAccountRepository( 61 | users = listOf(personA, personB, personC) 62 | ) 63 | } 64 | } -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code6/StubOfficialAccountRepository.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code6 2 | 3 | import kotlinx.coroutines.delay 4 | import section12.code6.Follower 5 | import section12.code6.OfficialAccountRepository 6 | 7 | class StubOfficialAccountRepository( 8 | private val users: List 9 | ) : OfficialAccountRepository { 10 | override suspend fun searchByName(name: String): Array { 11 | delay(1000L) 12 | return users.filter { user -> 13 | user.name.contains(name) 14 | }.toTypedArray() 15 | } 16 | } -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code6/StubPersonAccountRepository.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code6 2 | 3 | import kotlinx.coroutines.delay 4 | import section12.code6.Follower 5 | import section12.code6.PersonAccountRepository 6 | 7 | class StubPersonAccountRepository( 8 | private val users: List 9 | ) : PersonAccountRepository { 10 | override suspend fun searchByName(name: String): Array { 11 | delay(1000L) 12 | return users.filter { user -> 13 | user.name.contains(name) 14 | }.toTypedArray() 15 | } 16 | } -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code7/StringStateHolderTestFail.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code7 2 | 3 | import kotlinx.coroutines.test.advanceUntilIdle 4 | import kotlinx.coroutines.test.runTest 5 | import org.junit.jupiter.api.Assertions.assertEquals 6 | import org.junit.jupiter.api.Test 7 | import section12.code7.StringStateHolder 8 | 9 | 10 | class StringStateHolderTestFail { 11 | @Test 12 | fun `updateStringWithDelay(ABC)가 호출되면 문자열이 ABC로 변경된다`() = runTest { 13 | // Given 14 | val stringStateHolder = StringStateHolder() 15 | 16 | // When 17 | stringStateHolder.updateStringWithDelay("ABC") 18 | 19 | // Then 20 | advanceUntilIdle() 21 | assertEquals("ABC", stringStateHolder.stringState) 22 | } 23 | } -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code7/StringStateHolderTestSuccess.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code7 2 | 3 | import kotlinx.coroutines.test.StandardTestDispatcher 4 | import org.junit.jupiter.api.Assertions 5 | import org.junit.jupiter.api.Test 6 | import section12.code7.StringStateHolder 7 | 8 | 9 | class StringStateHolderTestSuccess { 10 | @Test 11 | fun `updateStringWithDelay(ABC)가 호출되면 문자열이 ABC로 변경된다`() { 12 | // Given 13 | val testDispatcher = StandardTestDispatcher() 14 | val stringStateHolder = StringStateHolder( 15 | dispatcher = testDispatcher 16 | ) 17 | 18 | // When 19 | stringStateHolder.updateStringWithDelay("ABC") 20 | 21 | // Then 22 | testDispatcher.scheduler.advanceUntilIdle() 23 | Assertions.assertEquals("ABC", stringStateHolder.stringState) 24 | } 25 | } -------------------------------------------------------------------------------- /src/test/kotlin/chapter12/code8/BackgroundScopeTest.kt: -------------------------------------------------------------------------------- 1 | package chapter12.code8 2 | 3 | import kotlinx.coroutines.delay 4 | import kotlinx.coroutines.launch 5 | import kotlinx.coroutines.test.advanceTimeBy 6 | import kotlinx.coroutines.test.runTest 7 | import org.junit.jupiter.api.Assertions 8 | import org.junit.jupiter.api.Test 9 | 10 | class BackgroundScopeTest { 11 | @Test 12 | fun `코루틴이 실행 완료될 때까지 호출 스레드를 블로킹하는 runTest`() = runTest { 13 | println(Thread.currentThread()) 14 | } 15 | 16 | @Test 17 | fun `끝나지 않아 실패하는 테스트`() = runTest { 18 | var result = 0 19 | 20 | launch { 21 | while (true) { 22 | delay(1000L) 23 | result += 1 24 | } 25 | } 26 | 27 | advanceTimeBy(1500L) 28 | Assertions.assertEquals(1, result) 29 | advanceTimeBy(1000L) 30 | Assertions.assertEquals(2, result) 31 | } 32 | 33 | @Test 34 | fun `backgroundScope를 사용하는 테스트`() = runTest { 35 | var result = 0 36 | 37 | backgroundScope.launch { 38 | while (true) { 39 | delay(1000L) 40 | result += 1 41 | } 42 | } 43 | 44 | advanceTimeBy(1500L) 45 | Assertions.assertEquals(1, result) 46 | advanceTimeBy(1000L) 47 | Assertions.assertEquals(2, result) 48 | } 49 | } --------------------------------------------------------------------------------