- 학습 목표 달성 확인 목록
- [] 동기와 비동기 실행의 개념을 이해하는가?

동기:메서드를 호출하고 그 메서드에게 리턴을 받아야지 종료할 수 있다.
예)학생이 시험을 보고 그 시험지를 선생님에게 제출하여 점수를 매긴 후 그 시험지를 다시 학생이 받아야지 시험 결과를 확인할 수 있다.
비동기:메서드를 Thread기술로 구현하여 동시에 두 스레드가 실행되더라도 각자 종료가 가능하다.
예)은행에서 A계좌에서 1000원을 B계좌에게 보내면 A계좌는 출금을 하였으므로 종료
B계좌에 입금이 되었으므로 종료가 된다.
- [] 비동기 방식으로 실행할 때 문제점을 아는가?
여러 스레드가 동시에 일반적인 add()메서드와 같이 값을 변경하는 메서드를 사용하면 값이 훼손되는 경우가 있다.
- [] critical section(= critical region) 용어를 설명할 수 있는가?
위의 설명처럼 한 메서드를 여러 스레드가 동시에 접근해서 문제가 발생하는 부분의 코드를 말한다.
- [] thread safe 용어를 설명할 수 있는가?
critical section(= critical region)의 반대인데
단지 데이터를 조회를 하는 데이터에 영향을 끼치지않는 부분의 코드를 말한다.
- [] 동기화 기법인 mutex, semaphore(n)
mutex:Mutual Exclusion의 약자이며
critical section(= critical region)에 오직 한 개의 스레드만 접근이 가능하며 emaphore(1)과 같은 개념이다.
emaphore(n):critical section(= critical region)에 n개의 스레드만 동시 접근을 허용한다.
- [] wait()/notify() 메서드를 사용할 수 있는가?
public static void main(String[] args) {
class ValueBox {
int count;
synchronized public void setCount(int count) {
this.count = count;
this.notify();//동기화 영역에서 호출해야 한다
}
}
class MyThread extends Thread {
ValueBox valueBox;
public void setValueBox(ValueBox valueBox) {
this.valueBox = valueBox;
}
@Override
public void run() {
System.out.println("스레드 시작했음!");
try {
while (true) {
System.out.println("스레드 대기중...");
//해당 객체에서 notify()를 통해 알림이 오기전까지 실행을 멈추게 한다
//wait()/notify()는 반드시 동기화 영역 안에서 호출해야 한다
//여기서 동기화 부분이란 synchronized로 선언한 메서드를 말한다
synchronized (valueBox) {
valueBox.wait();//해당 객체는 신호가 오기 전까지 기다리고 있으라는 뜻이다.
//기다림을 끝내는 방법은
//notify()를 통해 얼음땡을 해줘야 한다
}
System.out.println("카운트 시작!");
for (int i = 0; i < valueBox.count; i++) {
System.out.println("==> " + i);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
ValueBox valueBox = new ValueBox();
MyThread t = new MyThread();
t.setValueBox(valueBox);
t.start();//이 스레드는 main 스레드가 실행하라고 신호를 줄 때까지 기다린다
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("카운트?");
String str = scanner.nextLine();
if (str.equals("quit")) {
break;
}
valueBox.setCount(Integer.parseInt(str));
}
System.out.println("main 스레드 종료!");
scanner.close();
}
- [] Pooling 의 개념을 이해하는가?

- [] 스레드풀의 동작원리를 이해하는가?

- [] 스레드풀을 만들고 사용할 수 있는가?
//고정크기 스레드풀
static class MyRunnable implements Runnable {
int millisec;
public MyRunnable(int millisec) {
this.millisec = millisec;
}
@Override
public void run() {
try {
System.out.printf("[%s] - 스레드 실행 중...\n",
Thread.currentThread().getName());
Thread.sleep(millisec);
System.out.printf("[%s] - 스레드 종료!\n",
Thread.currentThread().getName());
} catch (Exception e) {
System.out.printf("[%s] 스레드 실행 중 오류 발생!\n", Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 일단 스레드풀의 크기(3 개)만큼 작업 수행을 요청한다.
// - 작업은 큐에 등록된 순서대로 보관된다.
// - 스레드풀은 큐에서 작업을 꺼내 스레드에게 일을 시킨다.
//
executorService.execute(new MyRunnable(6000));
executorService.execute(new MyRunnable(3000));
executorService.execute(new MyRunnable(9000));
// 스레드풀의 크기를 초과해서 작업 수행을 요청한다면?
// - 놀고 있는 스레드가 없을 경우,
// 다른 스레드의 작업이 끝날 때까지 작업큐에 대기하고 있는다.
// - 작업을 끝낸 스레드가 생기면 큐에서 작업을 꺼내 실행한다.
//
executorService.execute(new MyRunnable(2000));
executorService.execute(new MyRunnable(4000));
System.out.println("main() 종료!");
}
//가변크기 스레드풀
static class MyRunnable implements Runnable {
int millisec;
public MyRunnable(int millisec) {
this.millisec = millisec;
}
@Override
public void run() {
try {
System.out.printf("%s 스레드 실행 중...\n",
Thread.currentThread().getName());
Thread.sleep(millisec);
System.out.printf("%s 스레드 종료!\n",
Thread.currentThread().getName());
} catch (Exception e) {
System.out.printf("%s 스레드 실행 중 오류 발생!\n", Thread.currentThread().getName());
}
}
}
public static void main(String[] args) throws Exception {
// 스레드의 수를 고정하지 않고 필요할 때마다 스레드를 생성하는 스레드풀이다.
// 물론 작업을 끝낸 스레드는 다시 사용할 수 있도록 pool에 보관한다.
ExecutorService executorService = Executors.newCachedThreadPool();
// 놀고 있는 스레드가 없으면 새 스레드를 생성한다.
//
executorService.execute(new MyRunnable(6000));
executorService.execute(new MyRunnable(3000));
executorService.execute(new MyRunnable(9000));
executorService.execute(new MyRunnable(2000));
// 작업을 끝낸 스레드가 생길 때까지 일부러 기다린다.
//
Thread.sleep(3000);
// 그러면 새 스레드를 생성하지 않고
// 작업을 끝낸 스레드가 요청한 작업을 처리한다.
//
executorService.execute(new MyRunnable(4000));
System.out.println("main() 종료!");
}