1. 코루틴 개념

  • 모든 코루틴은 CouroutineScope 안에서 돌아감
  • 코루틴의 핵심은 suspend
    • 함수의 동작을 일시중단이 가능
  • 코루틴 내에서 thread는 놀지않게함. 처리되는 쓰레드가 일시중단 될경우 다른 것들을 처리함

1.1 Builder

  • 코루틴 함수들을 시작하는 지점이라고 보는 것이 옳음.
  • 즉, 코루틴 함수 스택이 시작되는 지점.

1.1.1 launch

//구현부분
public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}
  • 독립적으로 실행되는 새로운 coroutineScope를 생성
  • Thread를 생성하는 것과 비슷함.
    • 즉, 코루틴을 실행한 thread를 멈추지 않음.
fun main()
{
  GlobalScope.launch {
      delay(1000L)
      println("aa")
  }
}

//위와 같이 할 경우 "aa"는 실행되지 않음. 왜냐면 프로그램이 끝나버리니까.
  • CoroutineScope의 확장함수
CoroutineScope.launch()

1.1.2 runBlocking

  • 코루틴을 실행한 thread를 중단(blocking) 시킴
    • 새로운 코루틴을 시작후 완료될 때까지 현재 스레드를 중단 가능한 상태로 블로킹
    • dispatcher를 쓰면 다른 스레드에서 실행시킬수 있지만, 어쨌든 해당 스레드는 블로킹됨
  • 프로그램이 끝나지 않도록 하는 목적으로만 쓰임(현재는 suspend와 runTest를 사용함…)
    • Main함수 → suspend
    • Unit test 함수 → runTest

<aside> 💡 runBlocking은 다른 코루틴의 자식이 될 수 없음.

</aside>

1.1.3 async

  • launch와 동일하지만 값을 반환함.
  • CoroutineScope의 확장함수
CoroutineScope.async()

1.2 CouroutineScope

  • runBlocking과 같이 중단시킴
    • 아직은 잘 모르겠으나 runBlocking보다 좋다고 함
    • runBLocking은 실행한 현재 thead를 block 시키지만 얘는 어쨌든 코루틴의 블럭을 써서 메인 스레드를 중지시키지 않는 느낌
suspend fun main(): Unit = coroutineScope {
  launch {
    delpy(1000L)
    println("world")
  }
  println("hello")
}

1.3 CoroutineContext

  • 코루틴의 데이터를 저장하고 전달하는 객체
    • 컬렉션으로 처리됨
    • data object
  • 코루틴의 부모자식 관게를 Context를 상속받는것임
  • Job, CorutineName, CoroutineDispatcher와 같은 객체들의 모음임

1.3.1 컨텍스트 합치기

    • 연산자를 지원하며 같은 내용은 덮어쓰기, 다른 내용은 추가됨
data class CoroutineName(
    val name: String
) : AbstractCoroutineContextElement(CoroutineName)
{
    override fun toString(): String = "CoroutineName($name)"

    companion object Key : CoroutineContext.Key<CoroutineName>
}

fun main() {
    val ctx1: CoroutineContext = CoroutineName("Name1")
    println(ctx1[CoroutineName]?.name)

    val ctx2: CoroutineContext = CoroutineName("Name2")
    println(ctx2[CoroutineName]?.name)

    val ctx3 = ctx1 + ctx2
    println(ctx3[CoroutineName]?.name)
}

1.4 Job

  • CoroutineContext에 포함된 인터페이스로써 state를 가짐
  • 수명을 가지고 있으며 취소가능
  • 모든 코루틴은 자신만의 job을 가지고 있으며, CoroutineContext에서 상속되지 않는 유일한 값임
  • 기본적으로 active한 상태에서 시작하지만 옵셔널하게 lazy 시작을 위해 new 상태로 시작할 수 있음
  • active 상태에서 job이 실행되고 코루틴은 Job을 실행할 수 있음

구분 내용

new 시작 가능한 상태
active JOB을 실행할 수 있는 상태
completing 잡을 완료하고 자식들을 기다리는 상태
completed 자식들도 완료된 상태
cancelling 에러 후처리 상태
canceled 에러 후처리 완료

1.4.1 Job Factory 함수

  • 아래와 같이 Job을 코루틴 없이 생성후 할당할 수 있음
  • 팩토리함수로 만들경우 자식들이 완료되도 completed 상태로 가지않고 active상태로 존재
    • 계속 다른 코루틴에서 사용될 수 있기 때문에
  • 따라서 아래와 같이 해줘야 됨
suspend fun main(): Unit = coroutineScope {
    val job = Job()
    launch(job) {
        delay(1000)
        println("Text 1")
    }
    launch(job) {
        delay(1000)
        println("Text 2")
    }

    job.children.forEach { it.join() }
}

1.4.2 Job의 취소

  • 취소될경우 cancelling 상태로 변경되고 exception이 떨어짐
  • 그래서 try catch로 잡기보단 finally로 후처리 되어야 할것들을 처리해주는게 좋음
suspend fun main(): Unit = coroutineScope {
    val job = Job()
    launch(job) {
        try {
            delay(1000)
            println("Text 1")
        }
        finally {
            println("finally")

            launch {
                println("will not be print")
            }

            withContext(NonCancellable) {
                println("will be print")
            }
        }
    }

    job.cancelAndJoin()
    println("done")
}
  • 취소된 context 내에서는 코루틴을 추가로 실행될 수 없음… 단 withContext를 통해 처리가능

1.4.3 invokeOnCompletion

  • completed 또는 canceled 되기전에 실행되어야 할 내용을 정의
job.invokeOnCompletion { cause: Throwable? ->
        println("job completed with $cause")
    }

1.5 예외처리

  • 기본적으로 모든 예외는 부모-자식간으로 다 전파됨.
  • 중단점이 없을경우 모든 코루틴에 에러가 전파되어 모든 코루틴이 취소됨.

1.5.1 supervisorJob()

  • 기본적으로 자식에서 발생하는 예외를 무시시킴
suspend fun main(): Unit = runBlocking {

    launch(SupervisorJob()) {
        try {
            delay(1000)
            println("Text 1")

            throw Exception("Exception 1")
        }
        finally {
            println("finally")

            launch {
                println("will not be print")
            }
        }
    }

    launch(SupervisorJob()) {
        delay(1000)
        println("Text 2")
    }
    println("done")
}

//done
//Text 2
  • supervisorJob은 단 하나의 자식만 가짐
    • 따라서 아래와 같이 쓸경우 exception이 전파됨
suspend fun main(): Unit = runBlocking {

    launch(SupervisorJob()) {
        launch {
            delay(1000)
            throw Exception("exception")
        }

        launch() {
            delay(2000)
            println("출력 안됨")
        }
    }
    delay(3000)
    println("done")
}
// Exception.....
//done
  • 코루틴 내에서 try - catch는 Exception을 핸들링 할 수 없음

(2) Flow

  • stream 처리와 비슷
  • 생성자 → 중간 → 소비자(collect)
  • 중간
    • filter
    • map
fun main(): Unit = runBlocking(Dispatchers.Default) {
    flow { // producer
        repeat(times = 3) { data ->
            delay(300L)
            emit(data)
            println("produced $data")
        }
    }.collect { data -> // consumer
        delay(700L)
        println("consumed $data")
    }
}

(3) channel

val channel = lifecycleScope.produce<Int>{
	repeat(5){
		channel.send(20 + it)
	}
}

lifecycleScope.launch {
	for(temperature in channel) {
		Log.d("TEST", "$temperature")
	}
}

vs Flow

val flow: Flow<Int> = flow {
	repeat(5){
		emit(20 + it)
	}
}

lifecycleScope.launch {
	flow.collect { temperature ->
		Log.d("TEST", "$temperature")
	}
}

→ Chanel은 수신하기도 전에 보내기 시작. hot

→ Flow는 수신쪽에서 collect를 호출해야 보내기 시작. cold

(4) dispatcher

  • 실제 thread에 코루틴을 실행시키는 것
    • thread pool 같은 느낌이 없지는 않음.
  • 각 플랫폼에 따라 정해진 값이 있음
    • android에 경우 main, io, default 등
    • 커스텀한 이름을 지정할 수 있음

(5) 에러처리

  • 전파
    • 자동으로 에러를 전파함.
    • launch, actor
  • 노출
    • await()를 호출하기 전까지는 전파안됨
    • async, produce

→ 위 두개의 차이로 인해 잘 핸들링 해야함.

→ 코루틴의 에러는 try - catch로 처리되지 않음. → 이미 전파됨.

  • supervisorJon
    • 상위로의 전파를 막음
    suspend fun main() {
        // Child Job #2
        CoroutineScope(Dispatchers.Default + coroutineExceptionHandler).launch {
            // Child Job #5
            launch(SupervisorJob()) {
                delay(300)
                throw Exception("first coroutine Failed")
            }.join()
            // Child Job #6
            launch(SupervisorJob()) {
                delay(400)
                println("second coroutine succeed")
            }.join()
        }.join()
    }
    
    Caught: first coroutine Failed
    second coroutine succeed
    
    Process finished with exit code 0
    
  • supervisorScope
    • 범위를 지정해서 쓸수도 있음
suspend fun main() {
    // Child Job #2
    CoroutineScope(Dispatchers.Default + coroutineExceptionHandler).launch {
        supervisorScope {
            // Child Job #5
            launch {
                delay(300)
                throw Exception("first coroutine Failed")
            }.join()
            // Child Job #6
            launch {
                delay(400)
                println("second coroutine succeed")
            }.join()
        }
    }.join()
}

2. Coroutine in Vertx

(1) CoroutineVerticle()

  • Verticle의 start, stop을 suspend 함수로 시작하게 해줌
    • 또한 내부적으로 coroutine context를 vertx eventloop로 처리될 수 있도록 기본 dispatcher를 처리
    • verticle 이외의 코루틴에 영향이 없도록 supervisorJob으로 error handling 처리함
  • 아래는 코드 일부
abstract class CoroutineVerticle : Verticle, CoroutineScope {

  private lateinit var vertxInstance: Vertx
  private lateinit var context: Context

  override val coroutineContext: CoroutineContext by lazy 
      { context.dispatcher() + SupervisorJob() }

  override fun init(vertx: Vertx, context: Context) {
    this.vertxInstance = vertx
    this.context = context
  }

  override fun getVertx(): Vertx = vertxInstance

  override fun start(startFuture: Promise<Void>?) {
    launch {
      try {
        start()
        startFuture?.complete()
      } catch (t: Throwable) {
        startFuture?.fail(t)
      }
    }
  }

  override fun stop(stopFuture: Promise<Void>?) {
    val job = coroutineContext.job
    launch {
      try {
        stop()
        stopFuture?.complete()
      } catch (t: Throwable) {
        stopFuture?.fail(t)
      } finally {
        job.cancel()
      }
    }
  }
  • vertx에서는 runBlocking을 쓰면 안됨.

대안

  • 기본적으로 coroutineVerticle로 시작하면 모든 function을 suspend로 만들수 있음
  • 그 외에 새로운 블럭을 만들어야 한다면
val result: Future<List<CardInfo>> = vertxFuture {
      service.getCardRecommList(pageNo, pageLimit, cate_grp)
    }

Java 8 (2014년 출시)

Java 8은 매우 중요한 릴리스로, 다음과 같은 주요 기능이 포함되었습니다:

  • 람다 표현식: 함수형 프로그래밍 스타일을 지원하여 코드의 간결함과 가독성을 높입니다.
  • 스트림 API: 컬렉션을 효율적으로 처리하기 위한 스트림 처리 기능을 제공합니다.
  • 디폴트 메서드: 인터페이스에 메서드 구현을 추가할 수 있습니다.
  • java.time 패키지: 날짜와 시간을 다루기 위한 새로운 API를 도입합니다.
  • 메타스페이스: PermGen 공간을 대체하여 메모리 관리를 개선합니다.

Java 11 (2018년 출시)

Java 11은 장기 지원(LTS) 버전으로, 여러 새로운 기능과 변경 사항을 포함하고 있습니다:

  • 새로운 API:
    • HttpClient API: HTTP 요청을 처리하기 위한 표준 API입니다.
    • 파일 읽기/쓰기 메서드: Files.readString(), Files.writeString() 메서드가 추가되었습니다.
  • Local-Variable Syntax for Lambda Parameters: 람다 파라미터에 var를 사용할 수 있습니다.
  • 런타임 및 도구 개선:
    • 일부 Java EE 및 CORBA 모듈이 제거되었습니다.
    • JEP 328: Flight Recorder가 포함되어 애플리케이션 성능 분석을 돕습니다.
  • ZGC (Z Garbage Collector): 저지연 수집을 위한 새로운 가비지 컬렉터입니다.
  • 기타:
    • var 키워드를 사용한 로컬 변수 타입 추론(자바 10에서 도입됨)이 람다 파라미터에서도 사용 가능해짐.
    • 새로운 String 메서드: isBlank(), lines(), strip(), repeat() 등.

Java 17 (2021년 출시)

Java 17은 또 다른 장기 지원(LTS) 버전으로, 여러 개선 사항과 새로운 기능이 추가되었습니다:

  • 새로운 기능 및 API:
    • Sealed Classes: 상속을 제한하여 특정 클래스만 상속할 수 있도록 합니다.
    • Pattern Matching for instanceof: 타입 캐스팅을 더 간편하게 합니다.
    • Record 클래스: 불변 데이터 객체를 쉽게 생성할 수 있는 방법을 제공합니다.
  • 성능 개선:
    • G1 및 ZGC 가비지 컬렉터의 성능이 향상되었습니다.
  • 라이브러리 개선:
    • RandomGenerator 인터페이스 추가 및 Random 클래스 개선.
    • Vector API(Incubator): 벡터 계산을 최적화합니다.
  • 언어 기능:
    • Text Blocks: 여러 줄 문자열 리터럴을 쉽게 작성할 수 있습니다.
    • Switch Expressions: 더 간결한 switch 문법을 제공합니다.
  • 삭제 및 비활성화된 기능:
    • Applet API 제거.
    • Security Manager 비활성화.

Java 21 (2023년 출시)

Java 21은 최신 버전으로, 다음과 같은 주요 변경 사항이 포함됩니다:

  • 새로운 기능:
    • Virtual Threads: 더 많은 동시성을 처리하기 위한 경량 스레드.
    • Structured Concurrency: 스레드 작업을 구조적으로 관리하는 새로운 API.
  • 언어 기능:
    • Pattern Matching for switch: 더 간결하고 강력한 switch 문법.
    • Record Patterns: 레코드 타입의 패턴 매칭을 지원.
  • 성능 및 안정성 개선:
    • ZGC의 성능 향상 및 안정성 개선.
    • 새로운 Foreign Function & Memory API(FFM): Java 프로그램에서 외부 메모리와 상호작용하는 새로운 방식.
  • API 및 도구 개선:
    • 더 많은 HTTP/2 및 HTTP/3 지원.
    • 새로운 Vector API 개선 및 표준화.
  • 기타:
    • 여러 개의 incubator 및 preview 기능이 포함되어 더 많은 피드백을 수집하고 있습니다.

"먹고 싶은거로 골라. 나는 짜장면" 부장님 왈,

"저도 짜장이요"

"저도요"

"저도"

 

"오케이 그럼 짜장면 5개"

 

로 대두될수 있는 우리나라 사회의 집단주의적 문화를 꼬집으며 합리적 개인주의자가 많아져야

우리나라 사회가 행복해지고 발전할 수 있다고 얘기한다.

 

예로부터 우리나라 사회에서는 튀는 행동을 금기시하였으며 개인은 사회에 동화되어야 한다는

그러한 다양성이 존중받지 못하는 사회는 많은 부작용을 만들었다.

 

모두가 같은목표를 가지고 있다보니 자연스레 서열화가 될 수 밖에 없고

또한 천부적으로 타고난 '남의 눈치보기' 로 인해 우월감과 열등감이

가득한 사회

 

그런 사회의 병적인 부분을 단적으로 보여주는 몇몇 사례들을 통해 작가는 이야기한다.

 

"집단이 우선시되지 않고 개인이 우선시 되지만, 서로에게 합리적인 개인주의가 만연할때

단일적이지 않은 다양성이 존중받는 사회가 되었을때

우리사회가 더 나은 행복한 사회로의 발전을 할 수 있지 않은가?"

 

 

생각해보기

  • 나는 개인주의자인가요? 집단주의자인가요?

  • 다양성이 존중받지 못하여 목표를 중단할 수 밖에 없었던 사례?

  • 90년대생이 온다라는 것과 연계하여 생각하였을때 90년대생들은 개인주의자들일까요?

+ Recent posts