Skip to the content.

- 학습 목표 달성 확인 목록

- [] 동기와 비동기 실행의 개념을 이해하는가?

동기:메서드를 호출하고 그 메서드에게 리턴을 받아야지 종료할 수 있다.

예)학생이 시험을 보고 그 시험지를 선생님에게 제출하여 점수를 매긴 후 그 시험지를 다시 학생이 받아야지 시험 결과를 확인할 수 있다.

비동기:메서드를 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() 종료!");
  }