코틀린은 자바와 달리 외부에서 클래스의 메소드를 추가 가능
원하는 메소드가 있지만 내가 설계한 클래스가 아닐 때 외부에서 메소드를 관리함
내 목적을 위해 외부에서 관리하기 때문에 원본 클래스의 일관성을 유지 가능
fun main() { // fun 클래스명.메소드명 = 실행 코드
fun Student.getGrade() = println("학생의 등급은 ${this.grade} 입니다")
var student = Student("참새", 10, "A+")
student.displayInfo()
student.getGrade()
}
class Student(name: String, age: Int, grade: String) {
var name: String
var age: Int
var grade: String
init {
this.name = name
this.age = age
this.grade = grade
}
fun displayInfo() {
println("이름은 ${name} 입니다")
println("나이는 ${age} 입니다")
}
}
동기적 프로그래밍
이라 한다.동기 프로그래밍
: 결과값이 리턴될 때까지 모든 작업 stop비동기 프로그래밍
: 결과값에 관계없이 다음 작업 수행프로그램은 하나의 메인 쓰레드(실행흐름)가 존재한다.
하나의 메인 쓰레드는 fun main(), 메인함수를 의미한다.
별도의 자식 쓰레드를 생성해서 동시에 로직을 실행할 수 있다.
코틀린은 thread
키워드를 사용
몬스터를 공격하고, 체력이 줄어들고, 효과음이 동시에 발생해야 함
경마 프로그램의 말들은 동시에 출발해야 함
쓰레드보다 더욱 경량화된 동시 처리 기능을 제공
최적화된 비동기 함수를 사용
하드웨어 자원의 효율적인 할당을 가능하게 한다.
안정적인 동시성, 비동기 프로그래밍을 가능하게 한다.
로직들을 협동해서 실행하자는 것이 목표이고 구글에서 적극 권장
코루틴은 빌더
와 함께 사용한다.
빌더의 종류
일반적으로 launch
와 async
빌더를 가장 많이 사용한다.
결과값이 없는
코루틴 빌더를 의미한다.
Job객체로 코루틴을 관리
Job객체의 함수:
join : 현재의 코루틴이 종료되기를 기다림
cancel : 현재의 코루틴을 즉시 종료함
결과값이 있는
코루틴이고 Deffered 타입으로 값을 리턴해준다.- 앱이 실행된 이후에 계속 수행되어야할 때 사용
- 필요할 때만 생성하고 사용 후에 정리가 필요
Dispatcher
로 지정할 수 있다.- UI와 상호작용하기 위한 메인쓰레드
- 네트워크나 디스크 I/O작업에 최적화되어있는 쓰레드
- 기본적으로 CPU최적화되어있는 쓰레드
- 안드로이드에서는 특히 Dispatcher 간의
변환
을 해야하는 작업을 고려해야 함 (withContext)
ex) IO에서 작업 처리했는데 결과값을 Main에서 보고 싶을 때
실습
JVM환경에서는 main이 종료되기 때문에 코루틴의 결과를 확인할 수 없음
-> job의 join 메소드로 해결
fun main(args: Array<String>) {
println("메인쓰레드 시작")
var job = GlobalScope.launch {
delay(3000)
println("여기는 코루틴...")
}
runBlocking {
job.join()
}
println("메인쓰레드 종료")
}
다른 코루틴 스코프를 사용했을 경우
fun main(args: Array<String>) {
println("메인쓰레드 시작")
var job = CoroutineScope(Dispatchers.Default).launch {
delay(3000)
println("여기는 코루틴...")
}
runBlocking {
job.join()
}
println("메인쓰레드 종료")
job.cancel()
}
여러 개의 코루틴을 사용할 수 있음
- 코루틴의 결과값을 리턴 받을 수 있음
- 결과값을 리턴 받아야하기 때문에 await은 일시 중단이 가능한 코루틴에서 실행가능함
fun main(args: Array<String>) {
println("메인쓰레드 시작")
var job = CoroutineScope(Dispatchers.Default).launch {
var fileDownloadCoroutine = async(Dispatchers.IO) {
delay(10000)
"파일 다운로드 완료"
}
var databaseConnectCoroutine = async(Dispatchers.IO) {
delay(5000)
"데이터베이스 연결 완료"
}
println("${fileDownloadCoroutine.await()}")
println("${databaseConnectCoroutine.await()}")
}
runBlocking {
job.join()
}
println("메인쓰레드 종료")
job.cancel()
}
둘 다 동시성 프로그래밍
을 위한 기술
동시성 프로그래밍 기술은 Context Switching
이 중요한 개념
- 최소 작업 단위: Thread
- 각 thread가 독립적인 Stack 메모리 영역을 가진다.
- 동시성 보장 수단 : Context Switching
- 운영체제 커널에 의한 context Switching을 통해 동시성을 보장
- 블로킹 (Blocking)
- Thread A가 Thread B의 결과를 기다리고 있음
- 이 때, Thread A는 블로킹 상태
- A는 Thread B의 결과가 나올 때까지 해당 자원을 사용하지 못 함
- Thread A가 Task 1을 수행하는 동안 Task 2 의 결과가 필요하면 Thread B를 호출해요
- 이때 Thread A는 블로킹 되고 Thread B로 프로세스간에 스위칭이 일어나 Task 2을 수행해요
- Task 2가 완료되면 Thead A로 다시 스위칭해서 결과 값을 Task 1에게 반환해요
- 이때 Task 3, Task 4는 A, B작업이 진행되는 도중에 멈추지 않고 각각 동시에 실행되게 되요
- 이때 컴퓨터 운영체제 입장에서는 각 Task를 쪼개서 얼마나 수행할지가 중요하겠죠?
- 그래서 어떤 쓰레드를 먼저 실행해야할지 결정하는행위를 스케쥴링이라고 해요
- 이러한 행위를 통해 동시성을 보장해요
- 최소 작업 단위 : Coroutine Object
- 여러 작업 각각에 Object를 할당
- Coroutine Object도 엄연한 객체이기 때문에 JVM Heap에 적재함 (코틀린 기준)
- 동시성 보장 수단 : Programmer Switching (No-Context Switching)
- 소스 코드를 통해 Switching 시점을 마음대로 정함 (OS는 관여하지 않음)
- Suspend (Non-Blocking)
- bject 1이 Object 2의 결과를 기다릴 때 Object 1의 상태는 Suspend로 변경된다.
- 그래도 Object 1을 수행하던 Thread는 그대로 유효함
- 그래서 Object 2도 Object 1과 동일한 Thread에서 실행된다.
- Coroutine은 작업 단위가 Object라고 했어요
- Task 1을 수행하다가 Task 2의 수행요청이 발생했다고 가정해볼게요
- 신기하게도 컨텍스트 스위칭 없이 동일한 Thread A에서 수행할 수 있어요
- Thread C처럼 하나의 쓰레드에서 여러 Task Object들을 동시에 수행할 수 있어요
- 이러한 특징때문에 코루틴을
Light-Weight Thread
라고 이야기해요
쓰레드나 코루틴은 각자의 방법으로 동시성을 보장하는 기술
코루틴은 Thread를 대체하는 기술이 아님 -> 하나의 Thread를 더욱 잘개 쪼개서 사용하는 기술
코루틴은 쓰레드보다 CPU 자원을 절약하기 때문에 Light-Weight Thread
라고 한다.
구글에서는 코틀린의 코루틴 사용을 적극 권장하고 있음