# [Java] Thread
์์ฆ OS๋ ๋ชจ๋ ๋ฉํฐํ์คํน์ ์ง์ํ๋ค.
๋ฉํฐํ์คํน์ด๋?
์๋ฅผ ๋ค๋ฉด, ์ปดํจํฐ๋ก ์์ ์ ๋ค์ผ๋ฉด์ ์น์ํ๋ ํ๋ ๊ฒ
์ฝ๊ฒ ๋งํด์ ๋ ๊ฐ์ง ์ด์์ ์์ ์ ๋์์ ํ๋ ๊ฒ์ ๋งํ๋ค.
์ค์ ๋ก ๋์์ ์ฒ๋ฆฌ๋ ์ ์๋ ํ๋ก์ธ์ค์ ๊ฐ์๋ CPU ์ฝ์ด์ ๊ฐ์์ ๋์ผํ๋ฐ, ์ด๋ณด๋ค ๋ง์ ๊ฐ์์ ํ๋ก์ธ์ค๊ฐ ์กด์ฌํ๊ธฐ ๋๋ฌธ์ ๋ชจ๋ ํจ๊ป ๋์์ ์ฒ๋ฆฌํ ์๋ ์๋ค.
๊ฐ ์ฝ์ด๋ค์ ์์ฃผ ์งง์ ์๊ฐ๋์ ์ฌ๋ฌ ํ๋ก์ธ์ค๋ฅผ ๋ฒ๊ฐ์๊ฐ๋ฉฐ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ ํตํด ๋์์ ๋์ํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๊ฒ ํ ๋ฟ์ด๋ค.
์ด์ ๋ง์ฐฌ๊ฐ์ง๋ก, ๋ฉํฐ์ค๋ ๋ฉ์ด๋ ํ๋์ ํ๋ก์ธ์ค ์์ ์ฌ๋ฌ๊ฐ์ ์ค๋ ๋๊ฐ ๋์์ ์์ ์ ์ํํ๋ ๊ฒ์ ๋งํ๋ค. ์ค๋ ๋๋ ํ๋์ ์์ ๋จ์๋ผ๊ณ ์๊ฐํ๋ฉด ํธํ๋ค.
# ์ค๋ ๋ ๊ตฌํ
์๋ฐ์์ ์ค๋ ๋ ๊ตฌํ ๋ฐฉ๋ฒ์ 2๊ฐ์ง๊ฐ ์๋ค.
๋๋ค run() ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉ ํ๋ ๋ฐฉ์์ด๋ค.
public class MyThread implements Runnable {
@Override
public void run() {
// ์ํ ์ฝ๋
}
}
public class MyThread extends Thread {
@Override
public void run() {
// ์ํ ์ฝ๋
}
}
# ์ค๋ ๋ ์์ฑ
ํ์ง๋ง ๋๊ฐ์ง ๋ฐฉ๋ฒ์ ์ธ์คํด์ค ์์ฑ ๋ฐฉ๋ฒ์ ์ฐจ์ด๊ฐ ์๋ค.
Runnable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ๊ฒฝ์ฐ๋, ํด๋น ํด๋์ค๋ฅผ ์ธ์คํด์คํํด์ Thread ์์ฑ์์ argument๋ก ๋๊ฒจ์ค์ผ ํ๋ค.
๊ทธ๋ฆฌ๊ณ run()์ ํธ์ถํ๋ฉด Runnable ์ธํฐํ์ด์ค์์ ๊ตฌํํ run()์ด ํธ์ถ๋๋ฏ๋ก ๋ฐ๋ก ์ค๋ฒ๋ผ์ด๋ฉํ์ง ์์๋ ๋๋ ์ฅ์ ์ด ์๋ค.
public static void main(String[] args) {
Runnable r = new MyThread();
Thread t = new Thread(r, "mythread");
}
Thread ํด๋์ค๋ฅผ ์์๋ฐ์ ๊ฒฝ์ฐ๋, ์์๋ฐ์ ํด๋์ค ์์ฒด๋ฅผ ์ค๋ ๋๋ก ์ฌ์ฉํ ์ ์๋ค.
๋, Thread ํด๋์ค๋ฅผ ์์๋ฐ์ผ๋ฉด ์ค๋ ๋ ํด๋์ค์ ๋ฉ์๋(getName())๋ฅผ ๋ฐ๋ก ์ฌ์ฉํ ์ ์์ง๋ง, Runnable ๊ตฌํ์ ๊ฒฝ์ฐ Thread ํด๋์ค์ static ๋ฉ์๋์ธ currentThread()๋ฅผ ํธ์ถํ์ฌ ํ์ฌ ์ค๋ ๋์ ๋ํ ์ฐธ์กฐ๋ฅผ ์ป์ด์์ผ๋ง ํธ์ถ์ด ๊ฐ๋ฅํ๋ค.
public class ThreadTest implements Runnable {
public ThreadTest() {}
public ThreadTest(String name){
Thread t = new Thread(this, name);
t.start();
}
@Override
public void run() {
for(int i = 0; i <= 50; i++) {
System.out.print(i + ":" + Thread.currentThread().getName() + " ");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
# ์ค๋ ๋ ์คํ
์ค๋ ๋์ ์คํ์ run() ํธ์ถ์ด ์๋ start() ํธ์ถ๋ก ํด์ผํ๋ค.
Why?
์ฐ๋ฆฌ๋ ๋ถ๋ช run() ๋ฉ์๋๋ฅผ ์ ์ํ๋๋ฐ, ์ค์ ์ค๋ ๋ ์์ ์ ์ํค๋ ค๋ฉด start()๋ก ์์ ํด์ผ ํ๋ค๊ณ ํ๋ค.
run()์ผ๋ก ์์ ์ง์๋ฅผ ํ๋ฉด ์ค๋ ๋๊ฐ ์ผ์ ์ํ ๊น? ๊ทธ๋ ์ง ์๋ค. ๋ ๋ฉ์๋ ๋ชจ๋ ๊ฐ์ ์์ ์ ํ๋ค. ํ์ง๋ง run() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, ์ด๊ฑด ์ค๋ ๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์๋๋ค.
Java์๋ ์ฝ ์คํ(call stack)์ด ์๋ค. ์ด ์์ญ์ด ์ค์ง์ ์ธ ๋ช ๋ น์ด๋ค์ ๋ด๊ณ ์๋ ๋ฉ๋ชจ๋ฆฌ๋ก, ํ๋์ฉ ๊บผ๋ด์ ์คํ์ํค๋ ์ญํ ์ ํ๋ค.
๋ง์ฝ ๋์์ ๋ ๊ฐ์ง ์์ ์ ํ๋ค๋ฉด, ๋ ๊ฐ ์ด์์ ์ฝ ์คํ์ด ํ์ํ๊ฒ ๋๋ค.
์ค๋ ๋๋ฅผ ์ด์ฉํ๋ค๋ ๊ฑด, JVM์ด ๋ค์์ ์ฝ ์คํ์ ๋ฒ๊ฐ์๊ฐ๋ฉฐ ์ผ์ฒ๋ฆฌ๋ฅผ ํ๊ณ ์ฌ์ฉ์๋ ๋์์ ์์ ํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ฌ์ค๋ค.
์ฆ, run() ๋ฉ์๋๋ฅผ ์ด์ฉํ๋ค๋ ๊ฒ์ main()์ ์ฝ ์คํ ํ๋๋ง ์ด์ฉํ๋ ๊ฒ์ผ๋ก ์ค๋ ๋ ํ์ฉ์ด ์๋๋ค. (๊ทธ๋ฅ ์ค๋ ๋ ๊ฐ์ฒด์ run์ด๋ผ๋ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒ ๋ฟ์ด๊ฒ ๋๋ ๊ฒ..)
start() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด, JVM์ ์์์ ์ค๋ ๋๋ฅผ ์ํ ์ฝ ์คํ์ ์๋ก ๋ง๋ค์ด์ฃผ๊ณ context switching์ ํตํด ์ค๋ ๋๋ต๊ฒ ๋์ํ๋๋ก ํด์ค๋ค.
์ฐ๋ฆฌ๋ ์๋ก์ด ์ฝ ์คํ์ ๋ง๋ค์ด ์์ ์ ํด์ผ ์ค๋ ๋ ์ผ์ฒ๋ฆฌ๊ฐ ๋๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ start() ๋ฉ์๋๋ฅผ ์จ์ผํ๋ ๊ฒ์ด๋ค!
start()๋ ์ค๋ ๋๊ฐ ์์
์ ์คํํ๋๋ฐ ํ์ํ ์ฝ ์คํ์ ์์ฑํ ๋ค์ run()์ ํธ์ถํด์ ๊ทธ ์คํ ์์ run()์ ์ ์ฅํ ์ ์๋๋ก ํด์ค๋ค.
# ์ค๋ ๋์ ์คํ์ ์ด
์ค๋ ๋์ ์ํ๋ 5๊ฐ์ง๊ฐ ์๋ค
NEW : ์ค๋ ๋๊ฐ ์์ฑ๋๊ณ ์์ง start()๊ฐ ํธ์ถ๋์ง ์์ ์ํ
RUNNABLE : ์คํ ์ค ๋๋ ์คํ ๊ฐ๋ฅ ์ํ
BLOCKED : ๋๊ธฐํ ๋ธ๋ญ์ ์ํด ์ผ์์ ์ง๋ ์ํ(lock์ด ํ๋ฆด ๋๊น์ง ๊ธฐ๋ค๋ฆผ)
WAITING, TIME_WAITING : ์คํ๊ฐ๋ฅํ์ง ์์ ์ผ์์ ์ง ์ํ
TERMINATED : ์ค๋ ๋ ์์ ์ด ์ข ๋ฃ๋ ์ํ
์ค๋ ๋๋ก ๊ตฌํํ๋ ๊ฒ์ด ์ด๋ ค์ด ์ด์ ๋ ๋ฐ๋ก ๋๊ธฐํ์ ์ค์ผ์ค๋ง ๋๋ฌธ์ด๋ค.
์ค์ผ์ค๋ง๊ณผ ๊ด๋ จ๋ ๋ฉ์๋๋ sleep(), join(), yield(), interrupt()์ ๊ฐ์ ๊ฒ๋ค์ด ์๋ค.
start() ์ดํ์ join()์ ํด์ฃผ๋ฉด main ์ค๋ ๋๊ฐ ๋ชจ๋ ์ข ๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ ค์ฃผ๋ ์ผ๋ ํด์ค๋ค.
# ๋๊ธฐํ
๋ฉํฐ์ค๋ ๋๋ก ๊ตฌํ์ ํ๋ค๋ณด๋ฉด, ๋๊ธฐํ๋ ํ์์ ์ด๋ค.
๋๊ธฐํ๊ฐ ํ์ํ ์ด์ ๋, ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๊ฐ์ ํ๋ก์ธ์ค ๋ด์ ์์์ ๊ณต์ ํ๋ฉด์ ์์ ํ ๋ ์๋ก์ ์์ ์ด ๋ค๋ฅธ ์์ ์ ์ํฅ์ ์ฃผ๊ธฐ ๋๋ฌธ์ด๋ค.
์ค๋ ๋์ ๋๊ธฐํ๋ฅผ ์ํด์ , ์๊ณ ์์ญ(critical section)๊ณผ ์ ๊ธ(lock)์ ํ์ฉํ๋ค.
์๊ณ์์ญ์ ์ง์ ํ๊ณ , ์๊ณ์์ญ์ ๊ฐ์ง๊ณ ์๋ lock์ ๋จ ํ๋์ ์ค๋ ๋์๊ฒ๋ง ๋น๋ ค์ฃผ๋ ๊ฐ๋ ์ผ๋ก ์ด๋ฃจ์ด์ ธ์๋ค.
๋ฐ๋ผ์ ์๊ณ๊ตฌ์ญ ์์์ ์ํํ ์ฝ๋๊ฐ ์๋ฃ๋๋ฉด, lock์ ๋ฐ๋ฉํด์ค์ผ ํ๋ค.
# ์ค๋ ๋ ๋๊ธฐํ ๋ฐฉ๋ฒ
์๊ณ ์์ญ(critical section) : ๊ณต์ ์์์ ๋จ ํ๋์ ์ค๋ ๋๋ง ์ ๊ทผํ๋๋ก(ํ๋์ ํ๋ก์ธ์ค์ ์ํ ์ค๋ ๋๋ง ๊ฐ๋ฅ)
๋ฎคํ ์ค(mutex) : ๊ณต์ ์์์ ๋จ ํ๋์ ์ค๋ ๋๋ง ์ ๊ทผํ๋๋ก(์๋ก ๋ค๋ฅธ ํ๋ก์ธ์ค์ ์ํ ์ค๋ ๋๋ ๊ฐ๋ฅ)
์ด๋ฒคํธ(event) : ํน์ ํ ์ฌ๊ฑด ๋ฐ์์ ๋ค๋ฅธ ์ค๋ ๋์๊ฒ ์๋ฆผ
์ธ๋งํฌ์ด(semaphore) : ํ์ ๋ ๊ฐ์์ ์์์ ์ฌ๋ฌ ์ค๋ ๋๊ฐ ์ฌ์ฉํ๋ ค๊ณ ํ ๋ ์ ๊ทผ ์ ํ
๋๊ธฐ ๊ฐ๋ฅ ํ์ด๋จธ(waitable timer) : ํน์ ์๊ฐ์ด ๋๋ฉด ๋๊ธฐ ์ค์ด๋ ์ค๋ ๋ ๊นจ์
# synchronized ํ์ฉ
synchronized๋ฅผ ํ์ฉํด ์๊ณ์์ญ์ ์ค์ ํ ์ ์๋ค.
์๋ก ๋ค๋ฅธ ๋ ๊ฐ์ฒด๊ฐ ๋๊ธฐํ๋ฅผ ํ์ง ์์ ๋ฉ์๋๋ฅผ ๊ฐ์ด ์ค๋ฒ๋ผ์ด๋ฉํด์ ์ด์ฉํ๋ฉด, ๋ ์ค๋ ๋๊ฐ ๋์์ ์งํ๋๋ฏ๋ก ์ํ๋ ์ถ๋ ฅ ๊ฐ์ ์ป์ง ๋ชปํ๋ค.
์ด๋ ์ค๋ฒ๋ผ์ด๋ฉ๋๋ ๋ถ๋ชจ ํด๋์ค์ ๋ฉ์๋์ synchronized ํค์๋๋ก ์๊ณ์์ญ์ ์ค์ ํด์ฃผ๋ฉด ํด๊ฒฐํ ์ ์๋ค.
//synchronized : ์ค๋ ๋์ ๋๊ธฐํ. ๊ณต์ ์์์ lock
public synchronized void saveMoney(int save){ // ์
๊ธ
int m = money;
try{
Thread.sleep(2000); // ์ง์ฐ์๊ฐ 2์ด
} catch (Exception e){
}
money = m + save;
System.out.println("์
๊ธ ์ฒ๋ฆฌ");
}
public synchronized void minusMoney(int minus){ // ์ถ๊ธ
int m = money;
try{
Thread.sleep(3000); // ์ง์ฐ์๊ฐ 3์ด
} catch (Exception e){
}
money = m - minus;
System.out.println("์ถ๊ธ ์๋ฃ");
}
# wait()๊ณผ notify() ํ์ฉ
์ค๋ ๋๊ฐ ์๋ก ํ๋ ฅ๊ด๊ณ์ผ ๊ฒฝ์ฐ์๋ ๋ฌด์์ ๋๊ธฐ์ํค๋ ๊ฒ์ผ๋ก ์ฌ๋ฐ๋ฅด๊ฒ ์คํ๋์ง ์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ๋ค.
wait() : ์ค๋ ๋๊ฐ lock์ ๊ฐ์ง๊ณ ์์ผ๋ฉด, lock ๊ถํ์ ๋ฐ๋ฉํ๊ณ ๋๊ธฐํ๊ฒ ๋ง๋ฌ
notify() : ๋๊ธฐ ์ํ์ธ ์ค๋ ๋์๊ฒ ๋ค์ lock ๊ถํ์ ๋ถ์ฌํ๊ณ ์ํํ๊ฒ ๋ง๋ฌ
์ด ๋ ๋ฉ์๋๋ ๋๊ธฐํ ๋ ์์ญ(์๊ณ ์์ญ)๋ด์์ ์ฌ์ฉ๋์ด์ผ ํ๋ค.
๋๊ธฐํ ์ฒ๋ฆฌํ ๋ฉ์๋๋ค์ด ๋ฐ๋ณต๋ฌธ์์ ํ์ฉ๋๋ค๋ฉด, ์๋ํ๋๋ก ๊ฒฐ๊ณผ๊ฐ ๋์ค์ง ์๋๋ค. ์ด๋ wait()๊ณผ notify()๋ฅผ try-catch ๋ฌธ์์ ์ ์ ํ ํ์ฉํด ํด๊ฒฐํ ์ ์๋ค.
/**
* ์ค๋ ๋ ๋๊ธฐํ ์ค ํ๋ ฅ๊ด๊ณ ์ฒ๋ฆฌ์์
: wait() notify()
* ์ค๋ ๋ ๊ฐ ํ๋ ฅ ์์
๊ฐํ
*/
public synchronized void makeBread(){
if (breadCount >= 10){
try {
System.out.println("๋นต ์์ฐ ์ด๊ณผ");
wait(); // Thread๋ฅผ Not Runnable ์ํ๋ก ์ ํ
} catch (Exception e) {
}
}
breadCount++; // ๋นต ์์ฐ
System.out.println("๋นต์ ๋ง๋ฆ. ์ด " + breadCount + "๊ฐ");
notify(); // Thread๋ฅผ Runnable ์ํ๋ก ์ ํ
}
public synchronized void eatBread(){
if (breadCount < 1){
try {
System.out.println("๋นต์ด ์์ด ๊ธฐ๋ค๋ฆผ");
wait();
} catch (Exception e) {
}
}
breadCount--;
System.out.println("๋นต์ ๋จน์. ์ด " + breadCount + "๊ฐ");
notify();
}
์กฐ๊ฑด ๋ง์กฑ ์ํ ์ wait(), ๋ง์กฑ ์ notify()๋ฅผ ๋ฐ์ ์ํํ๋ค.