mutex ?

์›์ž์„ฑ ์œ„๋ฐ˜

์›์ž์„ฑ ์œ„๋ฐ˜์€ ๋™์‹œ์„ฑ ์˜ค๋ฅ˜ ์œ ํ˜•์ด๋‹ค. ์ด ์œ ํ˜•์˜ ์˜ค๋ฅ˜๋Š” ์ •ํ™•ํ•œ ๋™๊ธฐํ™” ์—†์ด ๊ฐ์ฒด์˜ ์ƒํƒœ๋ฅผ ๋™์‹œ์— ์ˆ˜์ •ํ•  ๋•Œ ๋ฐœ์ƒํ•œ๋‹ค. ์›์ž์„ฑ ์œ„๋ฐ˜์€ ์ฝ”ํ‹€๋ฆฐ์—์„œ๋„ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์˜ค๋ฅ˜๋ฅผ ํ”ผํ•  ์ˆ˜ ์žˆ๋„๋ก ๋””์ž์ธํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋Š” ๊ธฐ๋ณธํ˜•์„ ์ œ๊ณตํ•œ๋‹ค.

์›์ž์„ฑ์˜ ์˜๋ฏธ

์†Œํ”„ํŠธ์›จ์–ด ์‹คํ–‰์˜ ๊ด€์ ์—์„œ, ์—ฐ์‚ฐ(operation)์ด ๋‹จ์ผํ•˜๊ณ  ๋ถ„ํ• ํ•  ์ˆ˜ ์—†์„ ๋•Œ ์ด ์—ฐ์‚ฐ์„ ์›์ž์ (atomic)์ด๋ผ ํ•œ๋‹ค.

๋™์‹œ์„ฑ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๊ฒŒ ๋˜๋ฉด ๊ณต์œ  ์ƒํƒœ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ์ฝ”๋“œ ๋ธ”๋ก์ด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ ๋ณ€๊ฒฝ ์‹œ๋„์™€ ๊ฒน์น˜๋ฉด์„œ ์ด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

private var counter = 0

fun increment() {
		counter ++
}

์ˆ˜์ฐจ์ ์œผ๋กœ ์‹คํ–‰ํ•˜๋ฉด, counter ๊ฐ’์— ๋Œ€ํ•ด ๊ฑฑ์ •ํ•  ํ•„์š” ์—†์ด incremental()์„ ์›ํ•˜๋Š” ๋งŒํผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์—ฌ๊ธฐ์— ๋™์‹œ์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋‚ด๋ถ€์˜ ๋งŽ์€ ๊ฒƒ๋“ค์ด ๋ฐ”๋€๋‹ค.

var counter = 0

fun asyncIncrement(by: Int) = async(CommonPool[1]) {
		for (i in 0 until by) {
				counter++
		}
}

CommonPool์„ CoroutineContext๋กœ ์‚ฌ์šฉํ•ด ์š”์ฒญํ•œ ํšŸ์ˆ˜๋งŒํผ counter๋ฅผ ๋Š˜๋ฆฌ๊ณ  ์žˆ๋‹ค.

fun main(args: Array<String>) = runBlocking {
		val workerA = asyncIncrement(2000)
		val workerB = asyncIncrement(100)

		workerA.await()
		workerB.await()

		print("counter [$counter]")
}

์‹คํ–‰ ํ›„ counter์˜ ๊ฐ’์ด ๊ฐ€๋” 2100๋ณด๋‹ค ๋‚ฎ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

counter++๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์›์ž์ ์ด์ง€ ์•Š์•„์„œ ๋ฐœ์ƒํ•œ๋‹ค. ์ด๊ฒƒ์ด ์‹ค์ œ๋กœ ์˜๋ฏธํ•˜๋Š” ๋ฐ”๋Š” asyncIncrement ๋‚ด๋ถ€์˜ ์—ฌ๋Ÿฌ for ๋ฃจํ”„ ์ค‘ ์—ฌ๋Ÿฌ ์‚ฌ์ดํด์ด counter๊ฐ’์„ ์˜ค์ง ํ•œ ๋ฒˆ๋งŒ ๋ฐ”๊ฟจ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์ฝ”๋“œ ๋ธ”๋ก์„ ์›์ž์ ์œผ๋กœ ๋งŒ๋“ค๋ ค๋ฉด ๋ธ”๋ก ์•ˆ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์–ด๋–ค ๋ฉ”๋ชจ๋ฆฌ ์—‘์„ธ์Šค๋„ ๋™์‹œ์— ์‹คํ–‰๋˜์ง€ ์•Š๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.

์Šค๋ ˆ๋“œ ํ•œ์ •

์Šค๋ ˆ๋“œ ํ•œ์ •์˜ ๊ฐœ์š”

์Šค๋ ˆ๋“œ ํ•œ์ •์€ ์ด๋ฆ„์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด, ๊ณต์œ  ์ƒํƒœ์— ์ ‘๊ทผํ•˜๋Š” ๋ชจ๋“  ์ฝ”๋ฃจํ‹ด์„ ๋‹จ์ผ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋˜๋„๋ก ํ•œ์ •ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ์ƒํƒœ๊ฐ€ ๋” ์ด์ƒ ์Šค๋ ˆ๋“œ ๊ฐ„์— ๊ณต์œ ๋˜์ง€ ์•Š์œผ๋ฉฐ ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋งŒ ์ƒํƒœ๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค. ( ์ด ๋ฐฉ๋ฒ•์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์— ๋ถ€์ •์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์— ํ•œํ•ด์„œ๋งŒ ์œ ์šฉํ•˜๋‹ค.)

์•กํ„ฐ

์Šค๋ ˆ๋“œ ํ•œ์ •์€ ์•ž์—์„œ ์–ธ๊ธ‰ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋Š” ์ž˜ ์ž‘๋™ํ•˜์ง€๋งŒ ์•ฑ์˜ ์—ฌ๋Ÿฌ ๋‹ค๋ฅธ ๋ถ€๋ถ„์—์„œ ๊ณต์œ  ์ƒํƒœ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๊ฑฐ๋‚˜, ์›์ž ๋ธ”๋ก์— ๋” ๋†’์€ ์œ ์—ฐ์„ฑ์„ ์›ํ•˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค์˜ ๊ฒฝ์šฐ ์ด๋ฅผ ํ™•์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•˜๋‹ค.

์•กํ„ฐ์˜ ์—ญํ• 

์•กํ„ฐ๋Š” ๋‘ ๊ฐ€์ง€ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์˜ ์กฐํ•ฉ์ด๋‹ค. ์ƒํƒœ ์—‘์„ธ์Šค๋ฅผ ๋‹จ์ผ ์Šค๋ ˆ๋“œ๋กœ ํ•œ์ •ํ•˜๊ณ  ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ฑ„๋„๋กœ ์ƒํƒœ ์ˆ˜์ •์„ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋‹ค. ์•กํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ’์„ ์•ˆ์ „ํ•˜๊ฒŒ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ด๋ฅผ ์œ„ํ•œ ๊ฐ•๋ ฅํ•œ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ ๋งค์ปค๋‹ˆ์ฆ˜๋„ ๊ฐ–์ถ”๊ฒŒ ๋œ๋‹ค.

์•กํ„ฐ ์ƒ์„ฑ

์—ฌ๋Ÿฌ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ counter๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

private var counter = 0
private val context = newSingleThreadContext("counterActor")

fun getCounter() = counter

์ด์ œ ์นด์šดํ„ฐ์˜ ๊ฐ’์„ ์บก์Šํ™”ํ–ˆ์œผ๋ฏ€๋กœ ์ˆ˜์‹ ๋œ ๊ฐ ๋ฉ”์‹œ์ง€์— ๋”ฐ๋ผ ๊ฐ’์„ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ์•กํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

val actorCounter = actor<Void?>(context) {
		for (msg in channel) {
				counter++
		}
}

์ „์†ก๋œ ๋ฉ”์‹œ์ง€๋ฅผ ์‹ค์ œ๋กœ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ๋‹จ์ˆœํžˆ ์•กํ„ฐ์˜ ์œ ํ˜•์„ Void?๋กœ ์„ค์ •ํ•˜๋ฉด ํ˜ธ์ถœ์ž๊ฐ€ null์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

fun main(args: Array<String>) = runBlocking {
		val workerA = asyncIncrement(2000)
		val workerB = asyncIncrement(100)

		workerA.await()
		workerB.await()

		print("counter [$counter]")
}
fun asyncIncrement(by: int) = async(CommonPool) {
		for (i in 0 until by) {
				actorCounter.send(null)
		}
}

์•กํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ ๊ธฐ๋Šฅ ํ™•์žฅ

์•กํ„ฐ๋Š” ์‚ฌ์šฉํ•˜๋ฉด ์ฑ„๋„์ด ์ „์ฒด ์ฝ”๋ฃจํ‹ด์„ ์›์ž์ ์œผ๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ ๋” ๋†’์€ ์œ ์—ฐ์„ฑ์„ ํ—ˆ์šฉํ•œ๋‹ค๋Š” ์ ์ด ๊ฐ€์žฅ ์ปค๋‹ค๋ž€ ์žฅ์ ์ด๋‹ค.

์•กํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด ์นด์šดํ„ฐ๋ฅผ ๋Š˜๋ฆฌ๊ฑฐ๋‚˜ ์ค„์ผ ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด๋ณด์ž

enum class Action {
		INCREASE,
		DECREASE
}

๊ทธ๋Ÿฐ ๋‹ค์Œ์— ์•กํ„ฐ์™€ ์ฝ”๋ฃจํ‹ด์„ ์—…๋ฐ์ดํŠธํ•ด ์ด๋Ÿฐ ์•ก์…˜์„ ์—ฐ์‚ฐ์— ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

var actorCounter = actor<Action>(context) {
		for (msg in channel) {
				when(msg) {
						Action.INCREASE -> counter++
						Action.DECREASE -> counter--
				}
		}
}

fun asyncDecrement(by: Int) = async {
		for (i in 0 until by) {
				actorCounter.send(Action.DECREASE)
		}
}

๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” CommonPool์„ ์‚ฌ์šฉํ•ด ์นด์šดํ„ฐ๋ฅผ ๋™์‹œ์— ์ฆ๊ฐ€ ๋ฐ ๊ฐ์†Œ์‹œํ‚ค๋Š” ์ฝ”๋ฃจํ‹ด์„ ์ƒ์„ฑํ•˜๋ฉฐ, ์ฝ”๋ฃจํ‹ด์€ ์•ก์…˜๊ณผ ํ•จ๊ป˜ ๋ฉ”์‹œ์ง€๋ฅผ ์•กํ„ฐ์— ๋ณด๋‚ด์„œ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.

์•กํ„ฐ ์ƒํ˜ธ ์ž‘์šฉ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ •๋ณด

ํด๋ผ์ด์–ธํŠธ ๊ด€์ ์—์„œ ์•กํ„ฐ๋Š” ๋‹จ์ˆœํžˆ ์†ก์‹  ์ฑ„๋„์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ตฌํ˜„ ๊ด€์ ์—์„œ ์•กํ„ฐ์˜ ์ˆ˜ํ–‰๋ฐฉ์‹์„ ๊ณ ๋ฏผํ•ด ๋ณผ ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

๋ฒ„ํผ๋“œ ์•กํ„ฐ

์•กํ„ฐ๋Š” ๋‹ค๋ฅธ ์†ก์‹  ์ฑ„๋„๊ณผ ๋™์ผํ•˜๊ฒŒ ๋ฒ„ํผ๋ง๋  ์ˆ˜ ์žˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  ์•กํ„ฐ๋Š” ๋ฒ„ํผ๋ง๋˜์ง€ ์•Š๋Š”๋‹ค. ๋ฉ”์‹œ์ง€๊ฐ€ ์ˆ˜์‹ ๋  ๋•Œ๊นŒ์ง€ ๋ฐœ์‹ ์ž๋ฅผ send()์—์„œ ์ผ์‹œ ์ค‘๋‹จํ•œ๋‹ค. ๋ฒ„ํผ๋ง๋œ ์•กํ„ฐ(Buffered actors)๋ฅผ ์ƒ์„ฑํ•˜๋ ค๋ฉด capacity ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋นŒ๋”์— ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.

fun main(args: Array<String>) {
		val bufferedPrinter = actor<String>(capacity = 10) {
				for (msg in channel) {
						println(msg)
				}
		}

		bufferedPrinter.send("hello")
		bufferedPrinter.send("hello")

		bufferedPrinter.close()
}

CoroutineContext๋ฅผ ๊ฐ–๋Š” ์•กํ„ฐ

counter ์˜ˆ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ์•กํ„ฐ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ CoroutineContext๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

val dispatcher = newFixedThreadPoolContext(3, "pool")
val actor = actor<String>(dispatcher) {
		for (msg in channel) {
				println("Running in ${Thread.currentThread().name}")
		}
}

for (i in 1..10) {
		actor.send("a")
}

์ƒํ˜ธ ๋ฐฐ์ œ

์ง€๊ธˆ๊นŒ์ง€๋Š” ์ฝ”๋“œ ๋ธ”๋ก์˜ ๋ชจ๋“  ๋ฉ”๋ชจ๋ฆฌ ์•ก์„ธ์Šค๊ฐ€ ๋‹จ์ผ ์Šค๋ ˆ๋“œ์—์„œ ๋ฐœ์ƒํ•˜๋„๋ก ๋ณด์žฅํ•จ์œผ๋กœ์จ ์›์ž์„ฑ ์œ„๋ฐ˜์„ ํšŒํ”ผํ–ˆ๋‹ค. ๋‘ ๊ฐœ์˜ ์ฝ”๋“œ ๋ธ”๋ก์ด ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ํ”ผํ•  ์ˆ˜ ์žˆ๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. ๋ฐ”๋กœ ์ƒํ˜ธ ๋ฐฐ์ œ๋‹ค.

์ƒํ˜ธ ๋ฐฐ์ œ์˜ ์ดํ•ด

์ƒํ˜ธ ๋ฐฐ์ œ๋ž€ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ฝ”๋ฃจํ‹ด๋งŒ ์ฝ”๋“œ ๋ธ”๋ก์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๋™๊ธฐํ™” ๋งค์ปค๋‹ˆ์ฆ˜์„ ๋งํ•œ๋‹ค.

์ฝ”ํ‹€๋ฆฐ ๋ฎคํ…์Šค์˜ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ํŠน์ง•์€ ๋ธ”๋ก๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์ด๋‹ค. ์‹คํ–‰ ๋Œ€๊ธฐ ์ค‘์ธ ์ฝ”๋ฃจํ‹ด์€ ์ž ๊ธˆ์„ ํš๋“ํ•˜๊ณ  ์ฝ”๋“œ ๋ธ”๋ก์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์„ ๋•Œ๊นŒ์ง€ ์ผ์‹œ ์ค‘๋‹จ๋œ๋‹ค. ์ฝ”๋ฃจํ‹ด์€ ์ผ์‹œ ์ค‘๋‹จ๋˜์ง€๋งŒ ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋ฎคํ…์Šค๋ฅผ ์ž ๊ธ€ ์ˆ˜ ์žˆ๋‹ค.

๋ฎคํ…์Šค ์ƒ์„ฑ

๋ฎคํ…์Šค๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด Mutex ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋งŒ ์ƒ์„ฑํ•˜๋ฉด ๋œ๋‹ค.

var mutext = Mutext()

fun asyncIncrement(by: Int) = async {
		for (i in 0 until by) {
				mutex.withLock {
						counter++
				}
		}
}

ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ฝ”๋ฃจํ‹ด๋งŒ ์ž ๊ธˆ์„ ๋ณด์œ ํ•˜๊ณ , ์ž ๊ธˆ์„ ์‹œ๋„ํ•˜๋Š” ๋‹ค๋ฅธ ์ฝ”๋ฃจํ‹ด์„ ์ผ์‹œ ์ค‘๋‹จ ํ•จ์œผ๋กœ์จ ์นด์šดํ„ฐ์— ๋Œ€ํ•œ ๋ชจ๋“  ์ฆ๋ถ„์ด ๋™๊ธฐํ™” ๋˜๋„๋ก ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ช‡ ๋ฒˆ์„ ํ˜ธ์ถœํ•˜๋”๋ผ๋„ counter์˜ ์ฆ๋ถ„ ์ค‘ ์–ด๋Š ๊ฒƒ๋„ ์œ ์‹ค๋˜์ง€ ์•Š๋Š”๋‹ค.

์ƒํ˜ธ ๋ฐฐ์ œ์™€ ์ƒํ˜ธ ์ž‘์šฉ

๋Œ€๊ฐœ withLock()์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„ํ•˜๋‹ค. ์ž ๊ธˆ ๋ฐ ์ž ๊ธˆ ํ•ด์ œ์— ๋Œ€ํ•œ ์ƒ์„ธ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•˜๋ฉด lock, unlock()์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

val mutext = Mutext()

mutext.lock() // ์ž ๊ธˆ์ด ์ด๋ฏธ ์„ค์ •๋œ ๊ฒฝ์šฐ ์ผ์‹œ ์ค‘๋‹จ๋œ๋‹ค. 
print("I am now an atomic block")
mutext.unlock() // ์ด๊ฒƒ์€ ์ค‘๋‹จ๋˜์ง€ ์•Š๋Š”๋‹ค. 

isLocked ์†์„ฑ์„ ์‚ฌ์šฉํ•ด ๋ฎคํ…์Šค๊ฐ€ ํ˜„์žฌ ์ž ๊ฒจ ์žˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฎคํ…์Šค๋ฅผ ์ž ๊ธ€ ์ˆ˜ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ถˆ๋ฆฌ์–ธ(Boolean)์„ ๋ฐ˜ํ™˜ํ•˜๋Š” tryLock()์„ ์‚ฌ์šฉํ•˜๊ธฐ๋„ ํ•œ๋‹ค.

val mutext = Mutext()

mutext.lock() 
val lockedByMe = mutext.tryLock() // false
mutext.unlock() 

ํœ˜๋ฐœ์„ฑ ๋ณ€์ˆ˜

ํœ˜๋ฐœ์„ฑ ๋ณ€์ˆ˜๋Š” ๊ตฌํ˜„ํ•˜๋ ค๋Š” ์Šค๋ ˆ๋“œ ์•ˆ์ „(thread-safe) ์นด์šดํ„ฐ์™€ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์ง€ ๋ชปํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํœ˜๋ฐœ์„ฑ ๋ณ€์ˆ˜๋Š” ์ผ๋ถ€ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์Šค๋ ˆ๋“œ ๊ฐ„์— ์ •๋ณด๋ฅผ ๊ณต์œ ํ•ด์•ผ ํ•  ๋•Œ ๊ฐ„๋‹จํ•œ ์†”๋ฃจ์…˜์œผ๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค.

์Šค๋ ˆ๋“œ ์บ์‹œ

JVM์—์„œ ๊ฐ ์Šค๋ ˆ๋“œ๋Š” ๋น„ํœ˜๋ฐœ์„ฑ ๋ณ€์ˆ˜์˜ ์บ์‹œ๋œ ์‚ฌ๋ณธ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค. ์ด ์บ์‹œ๋Š” ํ•ญ์ƒ ๋ณ€์ˆ˜์˜ ์‹ค์ œ ๊ฐ’๊ณผ ๋™๊ธฐํ™”๋˜์ง€๋Š” ์•Š๋Š”๋‹ค. ํ•œ ์Šค๋ ˆ๋“œ์—์„œ ๊ณต์œ  ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ์บ์‹œ๊ฐ€ ์—…๋ฐ์ดํŠธ๋  ๋•Œ๊นŒ์ง€ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ๋Š” ๋ณผ ์ˆ˜ ์—†๋‹ค.

@Volatile

๋ณ€์ˆ˜์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์— ์ฆ‰์‹œ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ ์˜ˆ์ œ์—์„œ @Volatile ์ฃผ์„์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

@Volatile
var shutdownRequested = false

@Volatile์€ Kotlin/JVM์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํœ˜๋ฐœ์„ฑ(volatility)์„ ๋ณด์žฅํ•˜๋Š” ๊ธฐ๋Šฅ์„ JVM์˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋‹ค๋ฅธ ํ”Œ๋žซํผ์—์„œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

@Volatile์ด ์Šค๋ ˆ๋“œ ์•ˆ์ „ ์นด์šดํ„ฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์ง€ ๋ชปํ•˜๋Š” ์ด์œ 

๊ทธ๋Ÿฌ๋‚˜ ์ด์ „์˜ ์˜ˆ์ œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์‹ค์ œ ์ผ๋ถ€ ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์œ ์‹ค๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜จ๋ฐ์—๋Š” ๋‘ ๊ฐ€์ง€ ์ด์œ ๊ฐ€ ์žˆ๋‹ค.

  • ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ฝ๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๋Š” ๋™์•ˆ ์Šค๋ ˆ๋“œ์˜ ์ฝ๊ธฐ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ : ๋‘ ์Šค๋ ˆ๋“œ๋Š” ๋ชจ๋‘ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋กœ ์‹œ์ž‘ํ•ด ๋™์ผํ•œ ์ฆ๋ถ„์„ ๋งŒ๋“ ๋‹ค. ๋‘˜ ๋‹ค ์นด์šดํ„ฐ๋ฅผ X์—์„œ Y๋กœ ๋ณ€๊ฒฝํ•˜๋ฏ€๋กœ ํ•œ ์ฆ๋ถ„์”ฉ ์œ ์‹ค๋œ๋‹ค.

  • ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ˆ˜์ •ํ•œ ํ›„ ์Šค๋ ˆ๋“œ์˜ ์ฝ๊ธฐ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€๋งŒ, ์Šค๋ ˆ๋“œ์˜ ๋กœ์ปฌ ์บ์‹œ๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š์•˜์„ ๋•Œ: ์Šค๋ ˆ๋“œ๋Š” ๋กœ์ปฌ ์บ์‹œ๊ฐ€ ์ œ๋•Œ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š์•„์„œ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ Y๋กœ ์„ค์ •ํ•œ ํ›„์—๋„ ์นด์šดํ„ฐ์˜ ๊ฐ’์„ X๋กœ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค. ๋‘ ๋ฒˆ์งธ ์Šค๋ ˆ๋“œ๋Š” ์นด์šดํ„ฐ์˜ ๊ฐ’์„ ์ฆ๊ฐ€์‹œํ‚ค์ง€๋งŒ ์˜ค๋ž˜๋œ ๊ฐ’์œผ๋กœ ์‹œ์ž‘ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฏธ ๋ณ€๊ฒฝํ•œ ๋‚ด์šฉ์„ ๋ฎ์–ด ์“ด๋‹ค.

@Volatile์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ

ํœ˜๋ฐœ์„ฑ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋” ๋‚˜์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ ค๋ฉด ๋‘ ๊ฐ€์ง€ ์‹œ๋‚˜๋ฆฌ์˜ค ์ „์ œ๊ฐ€ ์ฐธ์ด์–ด์•ผ ํ•œ๋‹ค.

  • ๋ณ€์ˆ˜ ๊ฐ’์˜ ๋ณ€๊ฒฝ์€ ํ˜„์žฌ ์ƒํƒœ์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค.

  • ํœ˜๋ฐœ์„ฑ ๋ณ€์ˆ˜๋Š” ๋‹ค๋ฅธ ๋ณ€์ˆ˜์— ์˜์กดํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋‹ค๋ฅธ ๋ณ€์ˆ˜๋„ ํœ˜๋ฐœ์„ฑ ๋ณ€์ˆ˜์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค.

์ฒซ ๋ฒˆ์งธ ์ „์ œ๋Š” ์Šค๋ ˆ๋“œ ์•ˆ์ „ ์นด์šดํ„ฐ์™€ ๊ฐ™์€ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋ฐฐ์ œํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋œ๋‹ค. ๋ฐ˜๋ฉด์— ๋‘ ๋ฒˆ์งธ ์ „์ œ๋Š” ํœ˜๋ฐœ์„ฑ ๋ณ€์ˆ˜์˜ ์˜์กด์„ฑ ๋•Œ๋ฌธ์— ์ผ๊ด€์„ฑ ์—†๋Š” ์ƒํƒœ๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์„ ํ”ผํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค€๋‹ค. ์ด์ „ ์‚ฌ๋ก€๋Š” ๋‘ ๋ฒˆ์งธ ์ „์ œ๋ฅผ ๋”ฐ๋ฅด์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์•ˆ์ „ํ•˜์ง€ ์•Š๋‹ค.

class DataProcessor {
		@Volatile
		private var shutdownRequested = false

		fun shutdown() {
				shutdownRequested = true
		}

		fun process() {
				while (!shutdownRequested) {
						// process away
				}
		}
}

ํ•ด๋‹น ์˜ˆ์ œ์—์„œ๋Š” ๋‘ ์ „์ œ ๋ชจ๋‘ ์œ ํšจํ•˜๋‹ค.

  • shutdown()์—์„œ ์ž‘์„ฑ๋œ shutdownRequested์˜ ์ˆ˜์ •์€ ๋ณ€์ˆ˜ ์ž์ฒด์˜ ํ˜„์žฌ ์ƒํƒœ์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค. ํ•ญ์ƒ true๋กœ ์„ค์ •๋œ๋‹ค.

  • ๋‹ค๋ฅธ ๋ณ€์ˆ˜๋Š” shutdownRequested์— ์˜์กดํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋‹ค๋ฅธ ๋ณ€์ˆ˜์—๋„ ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค.

์›์ž์  ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ

์›์ž์„ฑ์€ ์›์ž์  ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋กœ์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์›์ž์  ์—ฐ์‚ฐ์„ ์ œ๊ณตํ•˜๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋‹ค.

val counter = AtomicInteger()
counter.incrementAndGet() // ์›์ž์ ์ด๋ฏ€๋กœ ์Šค๋ ˆ๋“œ ์•ˆ์ „ ์นด์šดํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. 

์š”์•ฝ

  • ๊ณต์œ  ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๋ฉด ๋™์‹œ์„ฑ ์ฝ”๋“œ์—์„œ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค. ์Šค๋ ˆ๋“œ์˜ ์บ์‹œ์™€ ๋ฉ”๋ชจ๋ฆฌ์˜ ์•ก์„ธ์Šค์˜ ์›์ž์„ฑ์œผ๋กœ ์ธํ•ด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ์ˆ˜ํ–‰ํ•œ ์ˆ˜์ • ์‚ฌํ•ญ์ด ์œ ์‹ค๋  ์ˆ˜ ์žˆ๋‹ค. ์ƒํƒœ์˜ ์ผ๊ด€์„ฑ์„ ํ•ด์น˜๋Š” ์›์ธ์ด ๋œ๋‹ค.

  • ์Šค๋ ˆ๋“œ ๋™๊ธฐํ™”๋ฅผ ํ•  ์ˆ˜ ์žˆ๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋งŒ ์ƒํƒœ์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋„๋ก ๋ณด์žฅํ•ด์„œ ์“ฐ๊ธฐ๊ฐ€ ์•„๋‹Œ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ๋งŒ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š”๊ฒƒ๊ณผ, ์ฝ”๋“œ ๋ธ”๋ก์„ ์›์ž์ ์œผ๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ ์ž ๊ธˆ์„ ์‚ฌ์šฉํ•ด ์ฝ”๋“œ ๋ธ”๋ก์„ ์‹คํ–‰ํ•˜๋ ค๋Š” ๋ชจ๋“  ์Šค๋ ˆ๋“œ์˜ ๋™๊ธฐํ™”๋ฅผ ๊ฐ•์ œํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

  • CoroutineContext๋ฅผ ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋กœ ๋œ ๋””์ŠคํŒจ์ฒ˜์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด ์ฝ”๋ฃจํ‹ด์˜ ์‹คํ–‰์„ ๋‹จ์ผ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋˜๋„๋ก ๊ฐ•์ œํ•œ๋‹ค.

  • ์•กํ„ฐ๋Š” ์†ก์‹  ์ฑ„๋„๊ณผ ์ฝ”๋ฃจํ‹ด์˜ ์Œ์ด๋‹ค. ์•กํ„ฐ๋ฅผ ๋‹จ์ผ ์Šค๋ ˆ๋“œ๋กœ ํ•œ์ •ํ•ด ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ธฐ ๋ณด๋‹ค๋Š” ๊ฐ•๋ ฅํ•œ ๋™๊ธฐํ™” ๋งค์ปค๋‹ˆ์ฆ˜์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๋‹ค. ์›ํ•˜๋Š” ์Šค๋ ˆ๋“œ์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด ๋ณ€๊ฒฝ์„ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ณ€๊ฒฝ์€ ํŠน์ • ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋  ๊ฒƒ์ด๋‹ค.

  • ์•กํ„ฐ๋Š” ํŠนํžˆ ์ฝ”๋ฃจํ‹ด์˜ ์Šค๋ ˆ๋“œ ์ œํ•œ๊ณผ ์Œ์„ ์ด๋ค„ ์ด์šฉํ•˜๋ฉด ์ข‹๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์•กํ„ฐ๊ฐ€ ์Šค๋ ˆ๋“œ ํ’€์—์„œ ์‹คํ–‰ํ•˜๋„๋ก ์•กํ„ฐ๊ฐ€ ์‚ฌ์šฉํ•  CoroutineContext๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์•กํ„ฐ๋Š” ์ฝ”๋ฃจํ‹ด์ด๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๋Ÿฌ ๋ฐฉ์‹์œผ๋กœ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์ž ๊ธˆ์„ ์‚ฌ์šฉํ•ด ์ฝ”๋ฃจํ‹ด์„ ๋™๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๋ฎคํ…์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฝ”๋ฃจํ‹ด์ด ๋™๊ธฐํ™”๋œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ์ฝ”๋ฃจํ‹ด์„ ์ผ์‹œ์ค‘๋‹จํ•  ์ˆ˜ ์žˆ๋‹ค.

  • JVM์€ ์Šค๋ ˆ๋“œ์˜ ์บ์‹œ์— ์ €์žฅ๋˜์ง€ ์•Š๋Š” ๋ณ€์ˆ˜์ธ ํœ˜๋ฐœ์„ฑ ๋ณ€์ˆ˜๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ํœ˜๋ฐœ์„ฑ ๋ณ€์ˆ˜๋Š” ์ˆ˜์ •๋  ๋•Œ ์ƒˆ ๊ฐ’์€ ์ด์ „ ๊ฐ’์— ์˜์กดํ•˜์ง€ ์•Š์œผ๋ฉฐ ํœ˜๋ฐœ์„ฑ ๋ณ€์ˆ˜์˜ ์ƒํƒœ๋Š” ๋‹ค๋ฅธ ์†์„ฑ์— ์˜์กดํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๋‹ค.

  • ์›์ž์  ๋ณ€์ˆ˜๋Š” ๋‹จ์ˆœํ•œ ๊ฒฝ์šฐ์— ์œ ์šฉํ•˜์ง€๋งŒ ๊ณต์œ ๋˜๋Š” ์ƒํƒœ๊ฐ€ ํ•˜๋‚˜ ์ด์ƒ์˜ ์—ฌ๋Ÿฌ ๋ณ€์ˆ˜์ธ ๊ฒฝ์šฐ ํ™•์žฅํ•˜๊ธฐ๊ฐ€ ์–ด๋ ค์šธ ๊ฒƒ์ด๋‹ค.

  • ์šฐ๋ฆฌ๋Š” ์ค‘์š”ํ•œ ์›์น™์ธ ์ •๋ณด ์€๋‹‰์„ ์‹ค์ฒœ์— ์˜ฎ๊ฒผ๋‹ค. ์นด์šดํ„ฐ์˜ ๊ตฌํ˜„์„ ์ˆจ๊ฒจ์„œ ํ–ฅํ›„์— ๋ฎคํ…์Šค, ์›์ž์  ๋ณ€์ˆ˜, ๋˜๋Š” ์•กํ„ฐ ์—†๋Š” ์Šค๋ ˆ๋“œ ์ œํ•œ์„ ์‚ฌ์šฉํ•˜๋„๋ก ๊ทธ๊ฒƒ์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.

Last updated