์๋ ํ์ธ์
์ค๋์ ์ฝํ๋ฆฐ 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์ ๋ํ ํฌ์คํ ์ ๋ง์น๊ฒ ์ต๋๋ค.
์ค๋๋ ์ฆ์ฝํ์ธ์ :)