๊พธ์ค€ํ•จ์ด ์ง„๋ฆฌ๋‹ค!!

์–ด์ œ๋ณด๋‹ค ๋ฐœ์ „ํ•œ ์˜ค๋Š˜์ด ๋˜๊ณ ํ”ˆ ๐Ÿง‘๐Ÿปโ€๐Ÿ’ป ์˜ ๋ธ”๋กœ๊ทธ

Tutorial/tutorial

[Kotlin] ์ฝ”ํ‹€๋ฆฐ Thread(์Šค๋ ˆ๋“œ)์— ๊ด€ํ•˜์—ฌ

๋ށ์š” 2022. 10. 4. 17:58

 

์•ˆ๋…•ํ•˜์„ธ์š” 

์˜ค๋Š˜์€ ์ฝ”ํ‹€๋ฆฐ Thread์— ๋Œ€ํ•˜์—ฌ ๊ณต๋ถ€ํ•œ ๋‚ด์šฉ์„

ํฌ์ŠคํŒ…ํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.

 

Thread(์Šค๋ ˆ๋“œ)๋ž€?

ํ•œ ๊ฐœ์˜ ํ”„๋กœ์„ธ์Šค ๋‚ด์—์„œ ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ์ž‘์—…์˜ ๋‹จ์œ„๋ฅผ ๋งํ•ฉ๋‹ˆ๋‹ค.

 

์•„๋ž˜์—์„œ ์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” ์–ด๋–ป๊ฒŒ Thread๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๐Ÿค”

 

๋จผ์ €, Threadํด๋ž˜์Šค ์ƒ์†์„ ํ†ตํ•ด ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

class SimpleThread(private val threadName : String , private val delayTime : Long) : Thread(threadName){
    var flag = false
    override fun run() {
        while (!flag){
            sleep(delayTime)
            println("ํ˜„์ œ ์“ฐ๋ ˆ๋“œ๋Š”${currentThread().name}์ž…๋‹ˆ๋‹ค.")
        }
    }
}

fun main() {
    val thread1 = SimpleThread("thread1" , 500L) // main thread์˜ sub thread๋ผ๊ณ  ๋งํ•œ๋‹ค.
    val thread2 = SimpleThread("thread2" , 1000L) // ํ˜น์€ background thread๋ผ๊ณ  ์นญํ•œ๋‹ค.

    thread1.start()
    thread2.start()

    try{
        Thread.sleep(5000)
        thread1.flag = true
        thread2.flag = true
    } catch (e : InterruptedException){
        //Todo Anything
    }
    println("Finish of ${Thread.currentThread().name}")
}

 

์œ„ ์ฝ”๋“œ๋Š” Threadํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์€ 'SimpleThread' ํด๋ž˜์Šค๋ฅผ main์—์„œ ์ƒ์„ฑํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

SimpleThread๋Š” flag๊ฐ€ true๊ฐ€ ๋  ๋•Œ๊นŒ์ง€ ์ „๋‹ฌํ•ด์ค€ delay๊ฐ’๋งŒํผ ํ…€์„ ์ฃผ๋ฉฐ ํ˜„์žฌ thread์˜ ์ด๋ฆ„์„ ์ถœ๋ ฅํ•˜๋„๋ก ํ•˜๋Š” ๋™์ž‘์ด๋ฉฐ

main thread์—์„œ 5์ดˆ ๋’ค์— ๋‘ SimpleThread์˜ flag๊ฐ’์„ true๋กœ ์ฃผ๋ฉด์„œ Thread๋ฅผ ์ข…๋ฃŒ์‹œํ‚ต๋‹ˆ๋‹ค.

 

์œ„์™€ ๊ฐ™์ด Thread class์—๋Š” run() , start()๊ฐ€ ์กด์žฌํ•˜๋Š”๋ฐ  start()๋ฅผ ํ˜ธ์ถœ ์‹œ์—๋Š” ์ž๋™์œผ๋กœ run()์ด ์‹คํ–‰๋˜๊ฒŒ ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค.

์•„์šธ๋Ÿฌ, Thread ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์•„ Thread๋ฅผ ๋งŒ ๋“ค ๊ฒฝ ์šฐ์—๋Š” ๋ฐ˜๋“œ์‹œ run()์„ ๊ตฌํ˜„ํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

 

๋‘ ๋ฒˆ์งธ Thread ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š”

Runnable interface๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ Thread์˜ ์ƒ์„ฑ์ž ์ธ์ž๋กœ ๋„˜๊ฒจ์ฃผ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

 

class SimpleRunnable(private val delayTime : Long) : Runnable {
    override fun run() {
        while(!Thread.interrupted()){
            try {
                Thread.sleep(delayTime)
                println(Thread.currentThread().name)
            } catch (e : InterruptedException){
                Thread.currentThread().interrupt()
                println("End of ${Thread.currentThread().name}")
            }
        }
    }
}

fun main() {
    val thread1 = Thread(SimpleRunnable(500L) , "์“ฐ๋ ˆ๋“œ1")
    val thread2 = Thread(SimpleRunnable(100L) , "์“ฐ๋ ˆ๋“œ2")

    thread1.start();thread2.start()

    try {
        Thread.sleep(5000)
        thread1.interrupt();thread2.interrupt()
    } catch (_ : InterruptedException){}
    println("End of ${Thread.currentThread().name}")
}

์œ„ ์˜ˆ์ œ์˜ ๊ฒฐ๊ด๊ฐ’์€ ๋™์ผํ•˜๋‚˜ Runnable์„ ๊ตฌํ˜„ํ•˜์—ฌ Thread์ƒ์„ฑ์ž์— ์ธ์ž๋กœ ๋„˜๊ฒจ์ฃผ๋Š”  ๋ฐฉ์‹์œผ๋กœ Thread๋ฅผ ์‚ฌ์šฉํ•œ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

( interrupt()๋ฅผ ํ˜ธ์ถœ ์‹œ์—๋Š” InterruptException์„ ๋‚ ๋ฆฌ๊ธฐ์— ๋ฐ˜๋“œ์‹œ catch์•ˆ์—์„œ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. )

 

์„ธ ๋ฒˆ์งธ๋กœ๋Š” thread()๋ฅผ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

import kotlin.concurrent.thread

fun main () {
    val thread1 = thread(start = false , name = "t500" ) {
        while(!Thread.interrupted()){
            try{
                Thread.sleep(500)
                println(Thread.currentThread().name)
            } catch (e : InterruptedException){
                Thread.currentThread().interrupt()
                println("End of ${Thread.currentThread().name}")
            }
        }
    }

    val thread2 = thread(start = false , name = "t1000"){
        while(!Thread.interrupted()){
            try{
                Thread.sleep(1000)
                println(Thread.currentThread().name)
            } catch (e : InterruptedException){
                Thread.currentThread().interrupt()
                println("End of ${Thread.currentThread().name}")
            }
        }
    }

    thread1.start(); thread2.start()
    try {
        Thread.sleep(5000)
        thread1.interrupt(); thread2.interrupt()
    } catch (e : InterruptedException){
        //Todo Anythings
    }
    println("End of ${Thread.currentThread().name}")
}

์œ„ ์˜ˆ์ œ ๋˜ํ•œ ๊ฒฐ๊ด๊ฐ’์€ ๋™์ผํ•˜๋‚˜ thread()์˜ ๋žŒ๋‹ค ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด Thread๋ฅผ ์ƒ์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค. 

 

์ด์–ด์„œ ๋งˆ์ง€๋ง‰์œผ๋กœ Thread Pool์„ ์ด์šฉํ•œ ๋ฐฉ๋ฒ•์ด ์กด์žฌํ•˜๋Š”๋ฐ

์—ฌ๊ธฐ์„œ Thread Pool(์Šค๋ ˆ๋“œ ํ’€)์ด๋ž€

์ž‘์—… ์ฒ˜๋ฆฌ์— ์‚ฌ์šฉ๋˜๋Š” Thread๋ฅผ ์ œํ•œ๋œ ๊ฐœ์ˆ˜๋งŒํผ ์ •ํ•ด๋†“๊ณ  ์ž‘์—… ํ(Queue)์— ๋“ค์–ด์˜ค๋Š” ์ž‘์—…๋“ค์„ ํ•˜๋‚˜์”ฉ Thread๊ฐ€ ๋งก์•„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ๋งํ•ฉ๋‹ˆ๋‹ค.

Thread Pool์„ ์‚ฌ์šฉ ์‹œ์—๋Š” ์ œํ•œ์ ์ธ ๋ฆฌ์†Œ์Šค์—์„œ ์Šค๋ ˆ๋“œ ๊ด€๋ฆฌ๊ฐ€ ์šฉ์ด ํ•ด์ง‘๋‹ˆ๋‹ค.

 

Thread Pool์˜ ์‚ฌ์šฉ์€ ExecutorService๋ฅผ ํ†ตํ•ด ๊ฐ€๋Šฅํ•˜๋ฉฐ

์•„๋ž˜ ํ•จ์ˆ˜๋“ค์„ ํ†ตํ•ด ์ƒ์„ฑ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

newFixedThredPool(n : Int) //n๊ฐœ์˜ thread๋ฅผ ๋งŒ๋“ ๋‹ค.

newCachedThreadPool() // Thread๋ฅผ ํ•„์š”ํ•˜๋‹ค๋ฉด ๊ณ„์† ์ƒ์„ฑ ,60์ดˆ ํ›„ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” Thread๋ฅผ ์ œ๊ฑฐ

newScheduledThreadPool(n : Int) // ์ฃผ๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š” Thread๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉ

newSingleThreadExecutor() // ํ•˜๋‚˜์˜ Thread๋งŒ์„ ์ด์šฉํ•ด์„œ ์ž‘์—…ํ• ๋•Œ ์‚ฌ์šฉ

 

๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ํ•œ๋ฒˆ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

class PoolTask(private val threadName : String , private val delay : Long , private var flag : Boolean) : Runnable{
    override fun run() {
        while (flag){
            try {
                Thread.sleep(delay)
                println("Running.. ${Thread.currentThread().name}")
            } catch (e : InterruptedException){
                flag = false
            }
        }
    }
}

fun main() {
    val executorService = Executors.newFixedThreadPool(5)

    for(i in 1..5){
        val task = PoolTask("Thread${i}" , (i*100).toLong() , true)
        executorService.submit(task)
    }

    if(executorService.awaitTermination(5 , TimeUnit.SECONDS)){
        println("5์ดˆ ์•ˆ์— ์ž‘์—…์ด ๋๋‚ฌ๋‹ค!")
    } else {
        println("์•„์ง ์ž‘์—…์ด ๋๋‚˜์ง€ ์•Š์•˜๋‹ค!!")
        executorService.shutdownNow()
    }
}

์œ„ ์˜ˆ์ œ๋Š” Runnable์„ ๊ตฌํ˜„ํ•œ PoolTask ํด๋ž˜์Šค๋ฅผ Thread Pool๋กœ ๋„˜๊ฒจ์ฃผ์–ด ์‹คํ–‰์‹œํ‚จ ํ›„

awaitTermination()์„ ํ†ตํ•ด 5์ดˆ ๋™์•ˆ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ์ข…๋ฃŒ๋˜์ง€ ์•Š์„ ๊ฒฝ์šฐ shutDownNow()์œผ๋กœ

ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ๋ชจ๋“  thread๋ฅผ ์ข…๋ฃŒ์‹œํ‚ต๋‹ˆ๋‹ค.

 

์ด์–ด์„œ Thread๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋™์‹œ์„ฑ ๋ฌธ์ œ์— ๋Œ€ํ•ด 

ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•๋“ค์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

Mutex ๊ธฐ๋ฒ•

์ž„๊ณ„ ์˜์—ญ(Critical Section)์— ๋™์‹œ์— ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•˜๋„๋ก ๋ง‰๋Š” ๊ธฐ๋ฒ• ์ค‘ ํ•˜๋‚˜๋กœ ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ์—์„œ ๋Œ€๊ธฐ ์ค‘์ธ Thread๋Š” ๋Œ€๊ธฐ ํ๋ฅผ ์ด์šฉ

 

Mutex๊ธฐ๋ฒ• ๊ฐ™์€ ๊ฒฝ์šฐ @Synchronized ํ‚ค์›Œ๋“œ๋ฅผ ํ†ตํ•ด ์ž„๊ณ„ ์˜์—ญ์— ์žˆ๋Š” ์†์„ฑ์ด๋‚˜ ํ•จ์ˆ˜๋“ค์— lock์„ ๊ฑธ์–ด ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

package com.lee.thraed

class SharedCounter {
    var count = 0
    fun increment() {
        count++
    }
}

class UnSafeAccess(private val counter : SharedCounter) : Thread() {
    override fun run() {
        for(i in 1 .. 1000000000){
            counter.increment()
        }
    }
}

fun main() {
    val counter = SharedCounter()

    val t1 = UnSafeAccess(counter)
    val t2 = UnSafeAccess(counter)

    t1.start();t2.start()

    try {
        t1.join()
        t2.join()
    } catch (e : InterruptedException) {
        //Todo Something
    }
    println("counter : ${counter.count}")
}

์œ„ ์˜ˆ์ œ๋Š” Thread๋ฅผ ์ƒ์†๋ฐ›์€ UnSafeAccess๋ฅผ ํ†ตํ•ด SharedCounter class์— ์žˆ๋Š” counter์— ์ ‘๊ทผํ•˜๋Š” ์ฝ”๋“œ๋กœ

๋™๊ธฐํ™” ์ฒ˜๋ฆฌ๋ฅผ ์ง„ํ–‰ํ•˜์ง€ ์•Š์•„ ๊ฐ Thread์—์„œ ๋™์‹œ์— ์ ‘๊ทผํ•˜๋ฉฐ ์˜๋„์™€๋Š” ๋‹ค๋ฅธ ๊ฐ’์ด ์ถœ๋ ฅ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๐Ÿ˜’

 

๋™๊ธฐํ™” ์ฒ˜๋ฆฌ๊ฐ€ ์ง„ํ–‰๋˜์ง€ ์•Š์•„ ๊ฐ’์ด ์žฌ๋Œ€๋กœ ์ถœ๋ ฅ๋˜์ง€ ์•Š์Œ

 

ํ•˜์ง€๋งŒ ์œ„ ์ฝ”๋“œ์— ๋™๊ธฐํ™” ์ฒ˜๋ฆฌ๋ฅผ ์ง„ํ–‰ํ•  ๊ฒฝ์šฐ

 

class SharedCounter {
    var count = 0
    @Synchronized // ๋™๊ธฐํ™” ์ฒ˜๋ฆฌ
    fun increment() {
        count++
    }
}

class UnSafeAccess(private val counter : SharedCounter) : Thread() {
    override fun run() {
        for(i in 1 .. 1000000000){
            counter.increment()
        }
    }
}

fun main() {
    val counter = SharedCounter()

    val t1 = UnSafeAccess(counter)
    val t2 = UnSafeAccess(counter)

    t1.start();t2.start()

    try {
        t1.join()
        t2.join()
    } catch (e : InterruptedException) {
        //Todo Something
    }
    println("counter : ${counter.count}")
}

 

๋™๊ธฐํ™”๋ฅผ ์ง„ํ–‰ํ•˜์—ฌ ์˜ฌ๋ฐ”๋ฅธ ๊ฐ’์„ ์ถœ๋ ฅ

 

์œ„์™€ ๊ฐ™์ด ์˜ฌ๋ฐ”๋ฅธ ๊ฐ’์„ ์–ป๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๐Ÿคฉ 

 

๋‹ค๋งŒ , ๋จผ์ € ์ž„๊ณ„ ์˜์—ญ์— ๋“ค์–ด๊ฐ„ Thread๊ฐ€ ํ•  ์ผ์„ ๋‹ค ๋๋‚ด๊ณ  ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€ lock์„ ๊ฑธ์–ด

๋‹ค์Œ Thread๋Š” ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•˜๋„๋ก ํ•˜๋Š” ๋ฐฉ์‹์ด๊ธฐ์—

lock์„ ๊ฑธ๊ณ  ํ’€๊ณ  ํ•˜๋Š” ๋™์ž‘๋“ค์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ ๋‹ค์†Œ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

Synchronize๋Š” ๋งค์šฐ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ ์ž˜๋ชป ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ Thread๋ผ๋ฆฌ ์„œ๋กœ lock์„ ๊ฑธ์–ด ๋Œ€๊ธฐ๋งŒ ํ•˜๋‹ค๊ฐ€

ํ”„๋กœ๊ทธ๋žจ์ด ๋ฉˆ์ถฐ๋ฒ„๋ฆฌ๋Š” DeadLock(๊ต์ฐฉ ์ƒํƒœ)์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•˜๋‹ˆ 

์‚ฌ์šฉํ•  ๋•Œ ๋งŽ์€ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

Semaphore ๊ธฐ๋ฒ•

๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๋‹ค์ˆ˜์˜ ์Šค๋ ˆ๋“œ๊ฐ€ n๊ฐœ์˜ ๊ณต์œ  ์ž์›์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ์ œํ•œํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋™๊ธฐํ™” ๊ธฐ๋ฒ•

 

์œ„ semaphore๊ธฐ๋ฒ•์€ ์•„์ง ์ถ”๊ฐ€์ ์ธ ๊ณต๋ถ€๊ฐ€ ํ•„์š”ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.. ๐Ÿ˜‚

 

์ข€ ๋” ๊ณต๋ถ€ํ•˜๋ฉฐ ์ถ”๊ฐ€ ํฌ์ŠคํŒ…ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์ด์ƒ์œผ๋กœ Kotlin Thread์— ๋Œ€ํ•œ ํฌ์ŠคํŒ…์„ ๋งˆ์น˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์˜ค๋Š˜๋„ ์ฆ์ฝ”ํ•˜์„ธ์š” :)