Skip to content

[이지훈] 2장 학습정리 #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/main/kotlin/ezhoon/chapter02/2-11.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ezhoon.chapter02

import java.util.concurrent.atomic.AtomicInteger

class AtomicConsumer {

@Volatile
var count = AtomicInteger(0)
private set

fun increment() {
count.incrementAndGet()
}
}
27 changes: 27 additions & 0 deletions src/main/kotlin/ezhoon/chapter02/2-12.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ezhoon.chapter02

import java.util.concurrent.Executors

fun main() {

val counter = AtomicConsumer()

val task = Runnable {
for (i in 0..9999) {
counter.increment()
}
}

val executorService = Executors.newCachedThreadPool()

val future1 = executorService.submit(task, true)
val future2 = executorService.submit(task, true)

if (future1.get() && future2.get()) {
println("${counter.count}")
} else {
println("실패")
}

executorService.shutdown()
}
31 changes: 31 additions & 0 deletions src/main/kotlin/ezhoon/chapter02/2-13-2.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ezhoon.chapter02

import java.util.concurrent.Executors

fun main() {

val point = Point()

val task = Runnable {
for (i in 0..9999) {
point.rightUp()
}
}

val executorService = Executors.newCachedThreadPool()

val future1 = executorService.submit(task, true)
val future2 = executorService.submit(task, true)
val future3 = executorService.submit(task, true)
val future4 = executorService.submit(task, true)
val future5 = executorService.submit(task, true)
val future6 = executorService.submit(task, true)

if (future1.get() && future2.get() && future3.get() && future4.get() && future5.get() && future6.get()) {
println("${point.getX()},${point.getY()}")
} else {
println("실패")
}

executorService.shutdown()
}
18 changes: 18 additions & 0 deletions src/main/kotlin/ezhoon/chapter02/2-13.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ezhoon.chapter02

import java.util.concurrent.atomic.AtomicInteger

class Point {

private val x = AtomicInteger(0)
private val y = AtomicInteger(0)

fun rightUp() {
x.incrementAndGet()
y.incrementAndGet()
}

fun getX() = x.get()

fun getY() = y.get()
}
12 changes: 12 additions & 0 deletions src/main/kotlin/ezhoon/chapter02/2-5.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ezhoon.chapter02

class Consumer {

@Volatile
var count: Int = 0
private set

fun increment() {
count++
}
}
27 changes: 27 additions & 0 deletions src/main/kotlin/ezhoon/chapter02/2-6.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ezhoon.chapter02

import java.util.concurrent.Executors

fun main() {

val counter = Consumer()

val task = Runnable {
for (i in 0..9999) {
counter.increment()
}
}

val executorService = Executors.newCachedThreadPool()

val future1 = executorService.submit(task, true)
val future2 = executorService.submit(task, true)

if (future1.get() && future2.get()) {
println("${counter.count}")
} else {
println("실패")
}

executorService.shutdown()
}
79 changes: 79 additions & 0 deletions src/main/kotlin/ezhoon/chapter02/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
## 람다식

> 함수형 인터페이스를 구현하기 위해 자바 8에서 도입한 표현식이다.

`( ) -> { 실행문 }`

### 함수형 인터페이스

| 인터페이스 | 설명 |
|--------------------|------------------------------|
| Function/Predicate | 인자를 전달받아 반환값을 반환하는 인터페이스 |
| BooleanSupplier | 인자 없이 반환값을 반환하는 인터페이스 |
| Action/Consumer | 반환값이 없는 인터페이스 |
| Cancellable | Action과 동일하나 실행 의미가 다른 인터페이스 |

## 비동기 처리

> 어떤 작업을 실행하는 동안에 해당 처리가 끝나기를 기다리지 않고 다른 작업을 시작할 수 있는 것


### 멀티 스레딩

> 스레드가 여러 개 존재할 때, 각각의 스레드에서 작업을 실행해 비동기 처리할 수 있다.

- 작업 1, 2, 3을 실행하는 프로그램이 존재한다.
- 하나의 싱글 스레드로 한다면
- 1 -> 2 -> 3 이렇게 순차적으로 진행이 될 것이다.
- 3개의 스레드가 있다면
- 1을 수행하다가 중간에 멈추고 2를 하고 중간에 멈추고 3을 하는 것이 가능해진다.
- 위의 싱글 스레드와 비슷하지만 네트워크 통신이나 DB 작업 같은 경우에는 유의미한 결과가 나온다.
- 멀티 프로세스라면
- 1, 2, 3이 거의 동시에 시작을 하고 작업의 양에 따라 끝나는 시점이 다르다.

> 근데 단일 프로세스여도 멀티 스레드면 그냥 동시에 시작이 가능하지 않나? 안되나?
> 백그라운드 스레드만 가능했던건가?

## 비동기 처리 시 주의할 점
> 싱글 스레드에서는 생각하지 않아도 되는 것들을 비동기 시점에선 알아야 할 것들이 존재하게 된다.

### 메모리와 캐시

- 클래스 필드가 가리키는 값과 실제 메모리가 가리키는 값이 동일하지 않을 수 있다.
- 필드가 다르는 값은 메모리에서 캐시된 값이다. 이 값을 변경하고 적절한 시점에 실제 메모리에 반영을한다.
- 여기서 주의해야 할 점이 적절한 시점이다.
- 만약 내가 반영하기 전에 다른 작업이 해당 값을 사용을 하게 된다면 문제가 될 것이다.

### 원자성
> 일련의 처리 흐름 중간에 다른 작업이 끼어들 가능성이 있는지 고려해야 하며 이 일련의 처리가 분할할 수 없게 되어 있는 것을 원장성이라고 한다.

- 비동기 처리에서는 특정 메서드를 실행하는 동안 다른 작업이 실행된다.
- 그러므로 필드에 접근하면 원장성이 깨지게 될 수 있다.

### 2-6 예제에서 매번 값이 다른 이유

<img src="https://user-images.githubusercontent.com/53300830/233787203-68a1b54e-0196-40df-bfcf-f29b10b693dc.png" width="500"/>

> 위의 사진과 비슷한 이치이다.

read -> modify -> write

- count의 값을 갖고 온다.
- 1을 더한다.
- count를 설정한다.

근데 두 개의 스레드가 동시에 count 값을 갖고 오거나 write 되기 전의 값을 갖고와서 작업을 하기에 문제가 되는 것이다.

## 비동기 처리 시 발생하는 문제에 대한 대응 방안

> 여러가지가 존재하는데 키워드로만 정리할 예정이다.

- final 제한자와 불변 객체
- Volatile
- 최신 메모리 값을 갖고 오는 것을 보장한다.
- 하지만 이 제한자는 최신 값을 갖고 올 뿐 업데이트할 때 원자성을 보장하지 않는다.
- Atomic
- 2-11, 2-12 예제 참고
- Synchronized